Delta 如何用链式复制同时做到强一致和高可用

Delta 用链式复制同时做到强一致和高可用,靠的是两条硬规则。写请求只在链尾确认,读请求默认只从链尾返回。数据在所有副本落盘之前不算写入成功,已确认的数据必然存在于每个副本,任何时刻读到的都是已提交版本。代价是写延迟略高于法定人数系统,换来的是无需选举、无需协商的确定性。做存储选型或设计复制协议的团队可参考。

什么是链式复制

链式复制把一组存储节点排成一条直线,第一个叫链首,最后一个叫链尾,中间节点按序转发。每条链按副本数由四个以上服务器组成,存储桶内有多条链,对象按名称做一致性哈希分片。链内节点分布在不同故障域,链首天然充当写领导者,不需要单独选主。

Delta 的写路径为什么慢而稳

  1. 客户端把写请求发给链首。
  2. 链首在本地落盘,同时把更新沿链向下转发,转发和落盘是流水线式的。
  3. 链尾在本地介质持久化之后,才向客户端确认写入完成。

只要有一个副本没来得及落盘,这次写就不会被确认。写成功的定义就是全部副本都拿到数据。写延迟因此比法定人数系统高,但换来确定的提交语义,读链尾永远等于读最新已提交版本。

Delta 的读路径如何兼顾扩展性

默认实现里读只打向链尾,保证返回的一定是已复制完成的干净版本。为了拉高开读吞吐,Delta 支持分摊查询,链上任意节点都能接读请求,非链尾节点应答前必须先向链尾确认该对象的最新已提交版本,脏版本绝不返回。额外调用比重读整个对象便宜,读吞吐随链长增长。

故障检测和恢复怎么做

  1. 同链节点用心跳和收发失败互相标记可疑成员,单条指控不算数,投票阈值设为二。
  2. 多数票确认不健康后,把该节点移出所在的链,转入自动修复。
  3. 修复优先让它回到原来的链尾位置,从上家增量拷贝缺失对象,期间仍接新写,读转给上游。
  4. 控制平面保证链在故障域间均匀分布,维持备用节点,链成员损失过半就补新备用。

超时阈值用压测调参,自动修复容忍误报。误检一个健康节点只触发一次多余的再同步。

和其他复制方式相比差在哪里

  • 对比主从复制。主从让所有副本直接服务读,裸读吞吐更高,Delta 靠分摊查询补这个差距,同时堵住脏读。
  • 对比共识协议。链式复制没有提议、投票、选主环节,链首就是写入口,协议面小,出错角落少。
  • 对比纠删码。Delta 每个节点存完整副本,存储效率不高,用简单换可靠,用延迟换确定。
  • 可用性。n 个节点的链至多容忍 n 减 2 个节点失效,与法定人数系统相当。

Delta 在 Meta 内部承担什么角色

Delta 是 Meta 基础设施栈最底层的低依赖对象存储,只提供 put、get、delete、list 四个操作,故意把功能砍到最少。它存构建产物和分发产物,blob 持续备份到冷归档存储并可持续恢复,重度退化环境下数据仍可找回。跨区域复制少数同步、其余异步,故障时先切走再回补。

常见问题

写延迟比法定人数方案高,为什么还要全链确认?

目标是确定性和简单性。全链确认之后,读任何已确认版本都不会碰到中间态,排查时不用猜哪个副本落后多少。

分摊查询会不会读到旧数据?

不会。非链尾节点必须向链尾核对最新干净版本,脏版本绝不返回。

我的业务适合照搬这套设计吗?

能接受写延迟换强一致、副本数较小、故障域清晰的场景,链式复制实现成本低,值得试。需要最优存储效率或全球毫秒级写,纠删码加共识更合拍。

链首挂了怎么办?

控制平面把失效节点移出链并触发修复,修复中的节点挂到链尾从上家补数据,链首角色随配置更新转移。