4.2 Leaderless 复制、Quorum 与副本修复
Leaderless 系统允许协调器把读写发送给一组副本,不依赖每个分片的固定 Leader。它可以在部分副本不可用时继续服务,但需要显式处理并发版本、过期副本和长期修复。
N、W、R 只描述确认集合
- N:一个分区的副本数;
- W:写成功需要的副本确认数;
- R:读返回需要的副本响应数。
若应答都来自同一固定集合的 N 个不同副本,W + R > N 保证读写集合相交,W > N/2 保证两个成功写集合相交。相交是确定的集合性质,但如何从交集中的版本得到正确读结果,仍取决于协议。不能把跨数据中心的总 N 直接用于 LOCAL_QUORUM,也不能把替补节点的 sloppy quorum 混进原集合计算。
看一个未做读修复的简化协议:N=3、R=W=2,写入新值只到 A 就超时。第一次读 A、B,选出 A 的新值;下一次读 B、C,却又返回旧值。两次读都有 quorum,历史仍无法线性化。这是解释公式缺口的反例,不是对某个 Cassandra 配置的故障实测;Cassandra 的 read_repair=BLOCKING 会为单调 quorum 读增加修复步骤,NONE 则不提供该项保证。
系统仍要解决:并发协调器、时钟冲突、失败写残留、读修复、删除和客户端重试。需要 compare-and-set 语义时,应使用产品明确提供的 LWT/共识路径。
Cassandra 写路径
协调器根据 partition key 找到副本,并等待请求 consistency level 所需的确认。副本通常先追加 commit log,再更新内存结构,之后刷成不可变 SSTable。
client
→ coordinator
├→ replica A: commit log + memtable
├→ replica B: commit log + memtable
└→ replica C: commit log + memtable确认数与落盘承诺也要分开。commitlog_sync=periodic 可在下一次日志同步前确认写入;batch、group 则等待相应同步后确认。多副本不自动消除所有确认副本同时掉电时的丢失窗口,应核对持久化配置和故障模型。
Memtable 刷盘后形成多个 SSTable,后台 compaction 合并它们。追加写提高吞吐,却把读放大、空间放大和 compaction 尾延迟带入运营成本。
读路径需要合并版本
协调器从满足 consistency level 的副本取得数据或摘要,按单元格时间戳等规则合并并返回结果,不是简单挑一份“最新整行”。过期副本可以通过 read repair、hinted handoff 和 anti-entropy repair 逐步追赶。
这些机制承担不同职责:
- hinted handoff 暂存对短时不可用副本的写提示;
- read repair 在读取时发现并修复差异;
- repair 系统性比较副本范围,修复长期分歧。
Hint 有保留期限,读修复又只覆盖被读取的数据,不能代替完整的 anti-entropy repair。应确保相关副本范围在 tombstone 的 gc_grace_seconds 窗口内完成修复,给故障和任务延迟留余量;只按时启动任务不算完成。长期离线副本错过删除且其他副本已回收标记时,旧数据可能重新出现,重新入群前需按恢复流程处理。
Last-write-wins 依赖时间戳语义
若冲突按时间戳选择最新值,时钟漂移或错误客户端时间可能让旧业务写覆盖新写。LWW 能确定收敛结果,不代表结果符合业务因果。例如较早的写入因快钟带上时间戳 1000,随后业务纠正写入却带上 900;按时间戳取大仍留下旧业务值。
单一写入所有者可减少并发来源,但服务端也可能钟不准;普通版本列不会自动变成 CAS。需要拒绝过期更新时,应使用真正的条件更新协议;需要合并时,应选择与业务相符的 CRDT 数据模型,不能只给现有字段贴个名字。选择前要区分:
- 同一字段的并发更新是否可以任意胜出;
- 删除和更新并发时谁赢;
- 合并是否满足交换、结合和幂等;
- 业务是否需要向用户暴露冲突。
分区设计决定性能上限
Cassandra 表围绕查询设计。Partition key 决定路由和数据共置,clustering columns 决定分区内排序。
过小分区增加元数据和跨分区扇出,过大分区造成热点、长 GC/compaction 和恢复困难。避免用 ALLOW FILTERING 掩盖未建模查询,也不要把跨分区 batch 当成一般事务。
删除比写入更复杂
分布式副本不能立即物理删除,否则离线副本恢复后会把旧值再次传播。删除先写 tombstone,再在满足修复、保留和 compaction 条件后回收。gc_grace_seconds 到期只是允许回收,不是到点立即删除;标记覆盖的旧值也不能留在未处理的 SSTable 中。
大量 tombstone 会增加读扫描和 compaction 压力。TTL、时间序列表和高频删除必须在 schema 阶段考虑,而不是上线后只调一个阈值。
监控信号
- 每个 consistency level 的延迟和不可用错误;
- 分区大小、热点 key 和 coordinator 扇出;
- pending compaction、SSTable 数和磁盘放大;
- repair 进度、最久未修复范围和 streaming 流量;
- tombstone 扫描、丢弃和 GC 窗口;
- dropped mutations、hint backlog 和副本版本差异。
下一课对比大文件系统和全球事务数据库,理解同为“分布式存储”为什么采用完全不同的协调成本。
参考资料
- Apache Cassandra, Dynamo architecture
- Apache Cassandra, Guarantees
- Apache Cassandra, Read repair
- Apache Cassandra, Tombstones
- Apache Cassandra, Commit log synchronization