Reviewed redo case

Apache Kafka

不是介绍它是什么,而是拆回它在压力、候选方案和债务之间做过的选择。

Kafka 的每一步都是在用"把这个东西也变成日志"解决上一个问题,但日志解决不了消费者协调、状态恢复和跨层读取延迟,所以今天的 Kafka 是地球上最好的日志,却不是最好的消息系统,也不是最好的流处理器。

pressure

LinkedIn 的数据量增长比现有管道(数据库、Hadoop、消息队列)的扩展速度快几个数量级,他们需要一个单一系统,既能喂饱实时消费者,又能从同一份数据中服务批处理系统。

chosen shape

用"消息持久化后可重放"换取了水平扩展和多消费者架构,但牺牲了队列"投递即删除"的简单心智模型。

debt

today pain

重平衡风暴

Orientation

先把问题放回原始约束里。

一个伪装成发布/订阅消息系统的分布式、分区化、可复制提交日志。

LinkedIn 的数据量增长比现有管道(数据库、Hadoop、消息队列)的扩展速度快几个数量级,他们需要一个单一系统,既能喂饱实时消费者,又能从同一份数据中服务批处理系统。

用追加式日志换取吞吐量,用可容忍的临时不可用换取磁盘效率,把复杂度从外部协调系统(ZooKeeper、事务协调器)推到日志本身。

Reading route

01约束先变硬
02局部选择胜出
03债务追到今天

Evolution stages

01

2010–2011

日志作为核心抽象

Source

LinkedIn 有大量数据源(页面浏览、搜索查询、用户行为)和大量下游消费者(监控、Hadoop ETL、实时分析、推荐系统)。维护 N 点对点的集成意味着每个新消费者都要触碰每个生产者。传统消息队列(ActiveMQ、RabbitMQ)无法跟上吞吐量,批处理系统(Hadoop)则滞后数小时。

A · rejected

扩展 ActiveMQ/RabbitMQ

单节点吞吐量天花板,运维成本高

队列针对短生命周期消息投递优化,不适合保留海量数据并服务多个以不同速度消费的生产者

B · rejected

构建日志聚合系统(如 Scribe/Flume)

解决摄入但缺实时 pub/sub 能力

日志聚合器写入优化但无消费者组抽象,无法做并行容错处理

C · chosen

将消息代理视为分布式提交日志

消费者必须自管偏移量;消息消费后不被删除

追加式日志复制极简单,顺序 I/O 带来高吞吐量,自然解耦生产者与 N 个独立消费者

Key trade-off

用"消息持久化后可重放"换取了水平扩展和多消费者架构,但牺牲了队列"投递即删除"的简单心智模型。

Debt trace

偏移量管理变成消费者的责任。早期版本将偏移量存 ZooKeeper,制造了协调瓶颈和脆弱依赖,移除花了数年。

02

2012–2013, Kafka 0.8

复制与 ISR 设计

Source

Kafka 早期部署在 LinkedIn 的裸金属通用硬件上。磁盘会坏,网络会分区,消息系统丢数据不可接受。需要复制能力,但不能压垮使 Kafka 可行的吞吐量。

A · rejected

多数派共识(Paxos/Raft)

容 1 故障需 3 副本,容 2 需 5 副本;延迟受最慢副本约束

多数派等待会摧毁顺序 I/O 优势,Kafka 要的是吞吐量不是低延迟共识

B · rejected

同步复制到所有 Follower

一个慢 Follower 拖死整个流水线

共享集群上的拖后腿节点很常见,无界尾部 latency 对高吞吐管道致命

C · chosen

Leader-Follower 配合动态 ISR

ISR 边界、高水位线、Leader 选举逻辑复杂;消费者只读到 HW

ISR 将持久性与可用性解耦:已提交写只需 ISR 成员确认,拖后腿节点不阻塞系统,ISR 动态收缩扩展

Key trade-off

用"尽力法定人数"换吞吐量和磁盘效率,代价是 ISR 掉空时分区可能拒绝写入,而非冒险丢数据。

Debt trace

ISR 机制和高水位线跟踪微妙且容易出错。分裂脑、Follower 数据截断、优先副本不均衡都是直接后果。控制器成为单点复杂度。ZooKeeper 作为元数据存储也成为后续扩展瓶颈。

03

2015, Kafka 0.9

消费者重写——Broker 端协调

Source

原始高级消费者用 ZooKeeper 协调分区分配。规模上去后出现惊群问题、分裂脑冲突(多个消费者认为拥有同一分区)、频繁重平衡失败需人工介入。

A · rejected

修补 ZK 算法

需复杂分布式锁;偏移量仍留在 ZK

ZK 根本不适合高变动消费者元数据,每次重平衡都猛击 ZK

B · rejected

用外部协调服务(etcd/Consul)

增加另一个有独立故障模式的依赖

Kafka 已有足够好的分布式日志,只是没用它做协调

C · chosen

将协调移入 Broker,偏移量存内部 Topic

Broker 需运行组协调器;重平衡逻辑服务端化;需新消费者协议

消除 ZK 瓶颈,故障检测集中到 Broker(本来就知道分区 Leadership),偏移量存入压缩 Kafka Topic 自然复制

Key trade-off

Broker 逻辑变重、消费者启动稍慢,换取消除一整类分布式协调缺陷,移除消费者对 ZK 的硬依赖。

Debt trace

消费者组协议复杂(JoinGroup/SyncGroup/Heartbeat/LeaveGroup)。重平衡仍暂停消费,"急切重平衡"会 stop-the-world。

mitigated: ZK 的负担)

04

2017, Kafka 0.11 / KIP-98

精确一次语义

Source

流处理应用需要从输入主题读取、更新状态、写入输出主题,不能在重试时重复记录,也不能在故障恢复时产生不一致。至少一次 + 幂等应用逻辑很难做对,把正确性负担推给每个开发者。

A · rejected

留给应用层去重

每个应用写自己的去重逻辑;外部数据库精确一次复杂且慢

Kafka→处理→Kafka 模式太常见,内建可减少重复 Bug

B · rejected

配合外部协调器的 2PC

需所有 Sink 参与;Kafka-to-Kafka 中 Broker 就是 Sink,不实用

外部协调器增加 latency 和新故障域,Kafka 是日志中心不是数据库中心

C · chosen

幂等生产者 + Broker 端事务协调器 + 原子多分区提交

Broker 需按分区跟踪生产者状态;事务日志增加写放大;消费者需选 read_committed

2PC 协调器保留在 Broker 集群内,利用现有日志抽象作事务日志,为 Kafka-to-Kafka 场景提供精确一次无需外部系统

Key trade-off

为生产者和 Broker 增加显著复杂度和约 3% 性能开销,换取让常见分布式模式无需自定义代码即可实现。

Debt trace

事务协调器是高吞吐事务负载的瓶颈。僵尸生产者隔离依赖生产者 Epoch,跨重启管理棘手。保证在 Kafka 边界终止,写入外部数据库仍需谨慎设计。

05

2016–2019, Kafka Streams / KSQL

嵌入流处理层

Source

Kafka 成为数据中枢后,团队开始构建流处理作业(Kafka → 处理 → Kafka)。当时用外部引擎(Spark Streaming、Storm、Flink)需单独集群、运维专长和复杂集成。

A · rejected

只推荐外部引擎

用户需运行另一个分布式系统;Kafka 只是数据源

对简单转换(过滤、映射、聚合)的长尾场景,单独集群的开销超过收益

B · rejected

在 Kafka 内建重量级处理集群

重复 Flink/Spark 复杂度;违背 Broker 简洁哲学

会膨胀 Broker 并在 Kafka 内造第二个系统

C · chosen

将流处理嵌入为客户端库,本地状态 + 变更日志容错

状态恢复需重放变更日志(慢);无真正检查点;扩展受分区数限制

每个应用变成自包含流处理器,无需单独集群。状态存 RocksDB,变更日志 Topic 通过 Kafka 复制提供容错。最小可行处理层

Key trade-off

无单独集群的操作简洁性,换比专用流处理器更弱的能力:无增量检查点、弱事件时间语义、恢复时间与状态大小成正比。

Debt trace

Kafka Streams 对 GB 级状态恢复极慢,不适合低 RTO。KSQL 继承了所有限制。它从未主导流处理市场——Flink 赢了复杂处理。

06

2019, Kafka 2.4 / KIP-429

粘性重平衡——缓解重平衡风暴

Source

D3 的急切重平衡在 Stage 3 引入后,生产环境中的重平衡风暴日益严重。消费者组的任何成员变动都触发全组停止消费、重新分配所有分区,在 K8s 弹性伸缩和频繁部署场景下成为常态痛点。

A · rejected

什么都不做,让用户控制重平衡频率

把运维复杂度推给用户;K8s 环境下频繁扩缩容无法避免重平衡

不解决根本问题,只是转移负担,用户体验差

B · rejected

完全服务端分配(一步到位到 KIP-848)

工程量太大,需重写整个消费者协议;当时团队专注 KRaft

2019 年无法完成,服务端分配需要更长时间设计和验证

C · chosen

合作式粘性重平衡(cooperative sticky)

协议更复杂;消费者和 Broker 都需升级;无法彻底消除重平衡暂停

只迁移真正需要变动的分区,保留大部分已有分配,显著减少重平衡时间和消费中断

Key trade-off

在不重写整个消费者协议的前提下,用更复杂的增量重平衡逻辑换取重平衡时间和中断的大幅减少。

Debt trace

(原 D3b) 粘性重平衡是补丁而非根治。协议更复杂,消费者必须同时支持急切和合作两种模式,非 Java 客户端实现困难。根本问题(分区所有权是客户端分布式共识)仍未解决。

mitigated: 部分缓解 D3(重平衡仍暂停消费,但时间和影响范围大幅缩小)

07

2019–2025, KIP-500

移除 ZooKeeper——KRaft

Source

ZK 从一开始就是 Kafka 的元数据存储。集群到数十万分区时 ZK 成为硬瓶颈:元数据写入慢、控制器故障转移需数秒、运维团队维护两个不同故障模式的分布式系统、分区天花板被 ZK 写入吞吐量限制。

A · rejected

优化 ZK 使用

KIP-500 已证明 ZK 根本不适合高变动元数据,修补是收益递减

ZK 的 ZAB 和 znode 为低速率协调设计,不适合来自数千分区的高频元数据更新

B · rejected

采用 etcd/Consul

仍是具有独立运维模型的外部依赖

Kafka 哲学是"日志即真理",换另一个外部系统会重复同样问题

C · chosen

内部 Raft 法定人数(KRaft),元数据存 __cluster_metadata 主题

巨大工程投入;需重写控制器和引导逻辑;迁移工具延迟多年

控制平面统一到 Kafka 自己的日志抽象,消除双系统运维负担,故障转移降至毫秒级,分区天花板到百万级,Kafka 终于自成一体

Key trade-off

数年工程 + 多版本迁移路径(2.8 引入,3.3 生产就绪,4.0 移除 ZK),移除自诞生就存在的依赖。

Debt trace

KRaft 控制器行为更新、更少经过十年生产验证。磁盘元数据快照增加新故障模式。ZK→KRaft 迁移工具复杂且对现有集群有风险。

mitigated: 作为元数据存储和协调瓶颈)

08

2021–2024

分层存储——冷热分离

Source

Kafka 集群的存储成本随数据保留期线性增长。企业要求数月甚至数年的数据保留用于合规和重放,但本地磁盘(尤其是 SSD)成本极高。云原生环境(K8s)要求更快的弹性扩缩容,而 Kafka 的"分区绑定到具体 Broker"模型阻碍了这一能力。

A · rejected

继续扩展本地磁盘

成本与保留期成正比;冷数据占用昂贵热存储

经济上不可持续,TB 级保留期使 Kafka 成为存储成本最高的组件之一

B · rejected

将冷数据移到独立归档系统

破坏 Kafka 的统一读取接口;消费者重放需对接两个系统

违背"日志是单一抽象"的哲学,增加操作复杂度

C · chosen

分层存储:热数据本地盘,冷数据透明转存对象存储(S3 等)

冷读取延迟增加;需管理本地与远程的数据生命周期;Broker 需处理多层级读取路径

对象存储成本比本地盘低 5-10 倍,保留期不再受磁盘约束。对大多数消费者透明,仅冷数据重放感受到延迟增加

Key trade-off

用冷读取的延迟增加和更复杂的读取路径,换取存储成本数量级下降和保留期灵活性。

Debt trace

冷热分层增加了读取路径的不可预测性。冷数据从 S3 拉取有显著延迟。Broker 启动时需处理多层状态。对象存储的可用性和速率限制成为 Kafka 的新外部依赖。

09

2023–2025, KIP-848, Kafka 4.0

新消费者协议——服务端分配

Source

D3(重平衡中断消费)经过粘性重平衡(Stage 6)部分缓解后,根本问题仍在:分区分配由客户端组 leader 计算,重平衡仍需全组参与。随着 KRaft 移除了 ZK,消费者协议成为最后一个依赖客户端分布式共识的子系统。

A · rejected

继续优化粘性重平衡

只能在客户端分配框架内打补丁;无法消除"组 leader 选举 + 全组同步"的基本结构

边际收益递减,无法解决根本的分布式共识问题

B · rejected

完全无状态消费者(类似 Pulsar 的 Broker 端游标)

需重写 Kafka 的存储和读取模型;与现有的分区-日志架构不兼容

架构改动太大,等同于重建一个不同系统

C · chosen

服务端分配:Broker 协调器直接计算并下发分区分配

Broker 协调器负载增加;协议复杂度转移但并未消除;需全新消费者协议

消除客户端 leader 选举和全组同步,分配由单一权威源(Broker 协调器)计算,支持增量再分配,消费中断最小化

Key trade-off

把分配逻辑从客户端移到服务端,用 Broker 协调器的额外负载换取消费者组彻底摆脱客户端分布式共识。

Debt trace

Broker 协调器现在成为消费者组的核心瓶颈。服务端分配的复杂度增加了 Broker 的 CPU 和内存压力。协议迁移需要所有消费者升级,混合版本集群的兼容性问题突出。

mitigated: 和 D3b(重平衡风暴和粘性重平衡的补丁局限性)——但并未完全消除重平衡概念本身。

mitigated: (原 D3b) 和 D3b(重平衡风暴和粘性重平衡的补丁局限性)——但并未完全消除重平衡概念本身。

Throughline

Kafka 的每个重大决策都是在问:能不能把这个东西也变成日志?

## 贯穿主线

追加式日志 > 队列语义

水平扩展 + 多消费者独立读取

Avoided: 单节点吞吐天花板、消费者速度耦合
Made harder: 偏移量管理、消息不删除导致的存储成本

动态 ISR > 严格多数派共识

高吞吐 + 可调 durability/availability 平衡

Avoided: 慢副本拖死流水线、磁盘空间浪费
Made harder: 分裂脑、优先副本不均衡、控制器复杂度

日志即协调器 > 外部协调服务

自包含系统、元数据规模上限提升

Avoided: 外部依赖的运维负担、惊群问题
Made harder: Broker 逻辑膨胀、新协议复杂度

客户端库 > 服务端处理集群

简单转换场景够用,复杂场景让位 Flink

Avoided: 单独集群的运维开销
Made harder: 大状态恢复慢、无事件时间语义

Transferable pattern

日志作为通用协调原语

Apache Kafka 的可迁移模式是把事件历史、复制、偏移量、事务和元数据尽可能收敛到日志抽象上。

Pulsar

围绕一个更窄的系统边界重新分配 Kafka 承担的复杂度。

都在处理吞吐、持久性、协调或状态恢复压力。

Flink

将状态恢复和流处理能力做成专用运行时。

都在处理吞吐、持久性、协调或状态恢复压力。

AutoMQ

围绕一个更窄的系统边界重新分配 Kafka 承担的复杂度。

都在处理吞吐、持久性、协调或状态恢复压力。

Redis Streams

围绕一个更窄的系统边界重新分配 Kafka 承担的复杂度。

都在处理吞吐、持久性、协调或状态恢复压力。

NATS JetStream

围绕一个更窄的系统边界重新分配 Kafka 承担的复杂度。

都在处理吞吐、持久性、协调或状态恢复压力。

Where it stops

低 RTO 的大状态流处理

使用带增量检查点和专用调度的流处理引擎。

Kafka Streams 从变更日志重放恢复,时间与状态大小成正比

需要存算彻底分离的云原生消息系统

从第一天就把存储段和计算节点拆开。

本地盘保留 TB 级数据成本极高,分层存储的冷读取又有延迟

Debt map

Resolved

stage-1

消费者偏移量存在 ZooKeeper

移至 __consumer_offsets 内部 Topic;协调移至 Broker 组协调器

stage-2

ZooKeeper 作为元数据存储成为扩展瓶颈

KRaft:内部 Raft + __cluster_metadata Topic,移除 ZK

stage-3

急切重平衡导致全组消费中断

新消费者协议(KIP-848):服务端分配,消除客户端 leader 选举和全组同步

stage-6

(原 D3b) 粘性重平衡只是补丁,协议更复杂

服务端分配彻底替代客户端分配

Unresolved

stage-4

事务协调器瓶颈

高吞吐事务负载触及天花板;僵尸生产者隔离偶尔配置错误导致重复或丢失

stage-5

Kafka Streams 大状态恢复极慢

10GB+ 状态恢复需 30-60 分钟,不适合低 RTO;严肃流处理用户直接选 Flink

stage-7

KRaft 新代码未经充分验证

生产环境中偶发的元数据不一致、快照损坏、迁移失败报告

stage-8

冷热分层读取不可预测

冷数据消费者经历显著延迟尖峰;Broker 启动时需重建多层状态

stage-9

新消费者协议增加 Broker 协调器负载

大规模消费者组场景下协调器可能成为新热点;混合版本升级复杂

stage-9

(原 D2-4) ISR/复制运维调优矩阵

配置错误的集群在边缘情况下仍丢数据或变得不可用

Pain ranking

01

重平衡风暴

消费者组任何成员变动都触发全组停止消费,大规模部署下频繁发生

attack angle: Pulsar 的 Broker 端游标管理完全避免此问题;新消费者协议(Kafka 4.0)大幅缓解但未根除

02

大状态恢复时间

Kafka Streams 从变更日志重放恢复,时间与状态大小成正比

attack angle: Flink 增量检查点无论状态大小数秒内恢复;Kafka Streams 被降级到简单转换

03

存储成本与保留期矛盾

本地盘保留 TB 级数据成本极高,分层存储的冷读取又有延迟

attack angle: Pulsar 段级存储 + 对象存储后端更优雅;云原生消息系统(如 AutoMQ)从架构上分离计算与存储

04

单分区吞吐量天花板

一个分区绑定一个 Leader Broker,热点分区无法横向拆分

attack angle: Pulsar 段级存储允许更细粒度负载均衡;Redis Streams 无此限制但牺牲持久性

05

事务协调器瓶颈

高吞吐事务场景下单 Broker 成为热点,精确一次配置复杂

attack angle: NATS JetStream 的更简单模型;部分用户退回到至少一次 + 幂等消费者

06

KRaft 迁移风险

ZK→KRaft 迁移工具复杂,生产集群切换需周密规划

attack angle: 新系统(如 Redpanda)从第一天就无外部依赖,无迁移包袱

Causal chain

Kafka 的每一步都是在用"把这个东西也变成日志"解决上一个问题,但日志解决不了消费者协调、状态恢复和跨层读取延迟,所以今天的 Kafka 是地球上最好的日志,却不是最好的消息系统,也不是最好的流处理器。

2011 LinkedIn 数据爆炸 | v 需要统一管道喂饱批处理 + 实时 | v 选择追加式日志 > 队列语义 -----> 解决:吞吐 + 多消费者解耦 | v 埋雷 D1: 偏移量管理成负担(存 ZK) | v 2012 需要复制保证持久性 | v 发明 ISR(主从 + 动态副本集) -----> 解决:不丢数据,不丢吞吐 | v 埋雷 D2: 分裂脑、控制器复杂度、ZK 瓶颈 | v 2015 ZK 消费者协调成为瓶颈 | v 协调移至 Broker + 偏移量存内部 Topic -> 偿还 D1 | v 埋雷 D3: 重平衡仍中断消费 | v 2017 至少一次对流处理不够 | v 幂等生产者 + 事务(KIP-98) --------> 解决:Kafka-to-Kafka 精确一次 | v 埋雷 D4: 事务协调器瓶颈 | v 2016 需要流处理,外部引擎太重 | v Kafka Streams 客户端库 ------------> 解决:简单转换无需单独集群 | v 埋雷 D5: 大状态恢复极慢 | v 2019 急切重平衡造成生产事故 | v 合作式粘性重平衡(KIP-429) --------> 部分缓解 D3 | v 埋雷 D3b: 补丁更复杂,未根治 | v 2019 ZK 本身成为扩展天花板 | v KRaft 内部共识 ---------------------> 偿还 D2(移除 ZK) | v 埋雷 D6: 新控制器未经充分验证 | v 2022 存储成本与保留期矛盾 | v 分层存储(本地 + 对象存储) ---------> 解决:保留期不再受磁盘约束 | v 埋雷 D7: 冷读取不可预测 | v 2025 新消费者协议(KIP-848) --------> 偿还 D3 和 D3b(服务端分配) | v 埋雷 D8: Broker 协调器成为新热点 | v 今天的 Kafka: 自成一体但流处理让给 Flink、 运维调优仍是一门手艺、 重平衡从客户端风暴变成服务端负载

Sources and inference notes

事实、推断和证据边界。

补充来源或修正

Evidence claims

factmediumstage-1

Apache Kafka 的 日志作为核心抽象 阶段由约束“LinkedIn 有大量数据源(页面浏览、搜索查询、用户行为)和大量下游消费者(监控、Hadoop ETL、实时分析、推荐系统)。维护 N 点对点的集成意味着每个新消费者都要触碰每个生产者。传统消息队列(ActiveMQ、RabbitMQ)无法跟上吞吐量,批处理系统(Hadoop)则滞后数小时。”驱动。

evidence 1 · basis 0

factmediumstage-2

Apache Kafka 的 复制与 ISR 设计 阶段由约束“Kafka 早期部署在 LinkedIn 的裸金属通用硬件上。磁盘会坏,网络会分区,消息系统丢数据不可接受。需要复制能力,但不能压垮使 Kafka 可行的吞吐量。”驱动。

evidence 1 · basis 0

factmediumstage-3

Apache Kafka 的 消费者重写——Broker 端协调 阶段由约束“原始高级消费者用 ZooKeeper 协调分区分配。规模上去后出现惊群问题、分裂脑冲突(多个消费者认为拥有同一分区)、频繁重平衡失败需人工介入。”驱动。

evidence 1 · basis 0

factmediumstage-4

Apache Kafka 的 精确一次语义 阶段由约束“流处理应用需要从输入主题读取、更新状态、写入输出主题,不能在重试时重复记录,也不能在故障恢复时产生不一致。至少一次 + 幂等应用逻辑很难做对,把正确性负担推给每个开发者。”驱动。

evidence 1 · basis 0

factmediumstage-5

Apache Kafka 的 嵌入流处理层 阶段由约束“Kafka 成为数据中枢后,团队开始构建流处理作业(Kafka → 处理 → Kafka)。当时用外部引擎(Spark Streaming、Storm、Flink)需单独集群、运维专长和复杂集成。”驱动。

evidence 1 · basis 0

factmediumstage-6

Apache Kafka 的 粘性重平衡——缓解重平衡风暴 阶段由约束“D3 的急切重平衡在 Stage 3 引入后,生产环境中的重平衡风暴日益严重。消费者组的任何成员变动都触发全组停止消费、重新分配所有分区,在 K8s 弹性伸缩和频繁部署场景下成为常态痛点。”驱动。

evidence 1 · basis 0

factmediumstage-7

Apache Kafka 的 移除 ZooKeeper——KRaft 阶段由约束“ZK 从一开始就是 Kafka 的元数据存储。集群到数十万分区时 ZK 成为硬瓶颈:元数据写入慢、控制器故障转移需数秒、运维团队维护两个不同故障模式的分布式系统、分区天花板被 ZK 写入吞吐量限制。”驱动。

evidence 1 · basis 0

factmediumstage-8

Apache Kafka 的 分层存储——冷热分离 阶段由约束“Kafka 集群的存储成本随数据保留期线性增长。企业要求数月甚至数年的数据保留用于合规和重放,但本地磁盘(尤其是 SSD)成本极高。云原生环境(K8s)要求更快的弹性扩缩容,而 Kafka 的"分区绑定到具体 Broker"模型阻碍了这一能力。”驱动。

evidence 1 · basis 0

factmediumstage-9

Apache Kafka 的 新消费者协议——服务端分配 阶段由约束“D3(重平衡中断消费)经过粘性重平衡(Stage 6)部分缓解后,根本问题仍在:分区分配由客户端组 leader 计算,重平衡仍需全组参与。随着 KRaft 移除了 ZK,消费者协议成为最后一个依赖客户端分布式共识的子系统。”驱动。

evidence 1 · basis 0

Inference notes