本页目录

存储系统实例

Dynamo(leaderless + gossip + quorum)和 Spanner(leader-based + TrueTime + Paxos + 2PC)代表分布式存储的两极。把前五章讲过的共识、复制、分区、成员发现拼成两个完整的系统架构。

概述

前面五章分别讲了共识复制分区成员发现读写路径与修复——这些不是孤立的概念,在真实的分布式存储系统里它们同时运转、互相咬合⁠。这篇选两个经典系统,把前面的零件拼成整车:Dynamo(leaderless + gossip + quorum 路线)和 Spanner(leader-based + TrueTime + 外部一致性 路线)。两个系统代表分布式存储的两极,看懂它们就懂了前面每一章为什么要那么讲。

Dynamo:可用性优先的拼图

Amazon 2007 年的 Dynamo 论文奠定了 leaderless + eventual consistency 这一整条路线(Riak、Cassandra、DynamoDB 都承自它)。它的架构是几个独立机制的组合⁠,每个机制前面都已单独讲过——把这几个拼起来,就是 Dynamo:

Dynamo 的做法对应前文章节
分区一致性哈希,每节点负责一段 token range;引入虚拟节点⁠(每物理节点 ~100 个 vnode)让负载更均匀一致性哈希
复制每条数据写到 coordinator 后,按顺时针方向写 N 个后继(vnode 所在物理节点),保证副本分散在不同物理节点复制策略 leaderless
coordinator 并发写 N 个副本,等 W 个 ACK 即返回;W < N 时部分副本暂时不一致分布式读写路径
coordinator 并发读 N 个副本,等 R 个响应,取版本最新的(比较向量时钟,不是时间戳);触发 read repair 异步推最新版本给落后副本同上
成员发现gossip 协议传播节点加入/离开/心跳信息,最终每个节点都有完整的路由表Gossip 协议
反熵后台 Merkle tree 比对, gossip 驱动修复反熵与数据修复
冲突解决向量时钟(vector clock)追踪因果;应用端在读取时合并冲突(购物车:合并不同副本的条目,别丢)冲突解决
hinted handoff目标节点不可达时,coordinator 把它的那份暂存在自己的 hints 区,等目标恢复后回放分布式读写路径 hint 段

这几层咬合时有两个全局约束贯穿全栈:

  • 始终可写(always writeable)——即使部分节点不可达,coordinator 也接受写(hinted handoff + sloppy quorum 保证 W 凑够),代价是冲突可能更频繁。
  • 最终一致性⁠——数据最终收敛,但收敛前的窗口内,不同读可能看到不同版本。应用层要处理这种事(Dynamo 把冲突推给应用端解决,这正是它设计哲学的核心:存储层不替应用做冲突决策)。

一个 Dynamo 写的完整旅程(把所有层串起来)

Dynamo 一次写的完整旅程:并发写三副本,W=2 达标即返回 put("cart:alice", {item:"book"}) Coordinator(任意节点) 已有全集群路由表(gossip 传播得来) hash("cart:alice") → token 落在 range [A, B] 查路由表:N=3 个 vnode 分别在物理节点 P1、P3、P7 P1、P3:并发写入成功 append WAL → fsync → write MemTable → ACK(version v5) P7:不可达 coordinator 把这份写进本地的 hinted handoff 区暂存 W=2 ACK 收到 → 返回 SUCCESS (后台异步) 下次 gossip 反熵触发 → Merkle tree 比对发现 P7 缺失 → 补齐 始终可写:P7 暂时不可达也不影响写入,hinted handoff + sloppy quorum 保证 W 照样凑够; 代价是短暂不一致——收敛靠后台反熵,而不是这次写就让所有副本立刻一致。

Spanner:外部一致性的拼图

Google Spanner(2012)选了另一条路:⁠强 leader + 外部一致性⁠,支撑 Google 的广告和 F1 数据库。它同样是一堆机制的组合,但追求的目标完全相反——⁠宁可牺牲可用性,也要保证"像单机一样"的事务语义⁠。

Spanner 的做法对应前文章节
分区范围分片(range sharding),key 按字典序切成 tablets,每个 tablet 是一个 Paxos group分片策略
共识每个 tablet 内部是一个 Paxos group(多副本,Paxos 选主并复制日志)Paxos
复制Paxos log 复制到 group 内 majority;所有写必须经 leader;读可以从 follower(但需要读事务时间戳⁠)复制策略 single-leader
事务跨 tablet 写走 2PC(两阶段提交),协调者是其中一个 Paxos leader;提交时间戳由 TrueTime 分配分布式事务
时间TrueTime:每个数据中心有 GPS + 原子钟对时,保证全局时钟偏移 ≤ ε(~7ms)。给每个事务分配一个"晚于所有已提交事务"的时间戳,实现外部一致性时间与时钟

Spanner 的核心创新是 TrueTime + 2PC + Paxos 的组合,实现 外部一致性(external consistency)——事务按提交时间戳全局有序,任何读都能看到所有早于它的写。这和 Dynamo 的"最终收敛、读可能看到旧数据"完全相反。

一个 Spanner 跨表事务(把所有层串起来)

BEGIN
  UPDATE users SET balance = balance - 100 WHERE id = 42  -- 在 tablet A (Paxos group G1)
  UPDATE orders SET status = 'paid' WHERE id = 789        -- 在 tablet B (Paxos group G2)
COMMIT

流程:
  1. Client → G1 的 Paxos leader (变成 2PC Coordinator)
  2. Coordinator: 发 prepare 到 G1 和 G2 的 leader
  3. 每个 group 的 Paxos leader: 把 prepare 记录写入 Paxos log → 复制到 majority
  4. 所有 participant ACK → Coordinator 选 commit timestamp:
     - ts = max(各 participant 本地时间) + 保底 > TrueTime.now() + ε
     - 这保证 ts 晚于任何已提交事务的时间戳 (外部一致性)
  5. Coordinator: 把 commit record + ts 写入自己的 Paxos log → 复制 → committed
  6. 通知各 participant 提交 (Paxos log entry with ts)

注意:第 4 步 TrueTime 的 now() + ε 保证——因为 ε 是时钟偏差上界,所有数据中心"此刻"的真实时间不可能超过 now() + ε,所以选 ts > now() + ε 保证 ts 在将来,任何读在 ts 后都能看到这个事务。这就是 Spanner 能对 SQL 提供"像单机一样"的序列化隔离——因为时间戳是真实的、物理约束的全局序。

两极对比

DynamoSpanner
一致性最终一致性外部一致性(强)
写可用性始终(leaderless,任何节点可接受)需要 leader(leader 挂了等选举)
冲突解决应用端 (向量时钟)存储层 (时间戳全局序)
事务不支持跨 key 事务完整 ACID (2PC + TrueTime)
延迟低(无多轮协调)高(Paxos + 2PC + commit wait)
时钟依赖宽松(只用于版本比较,不要求同步)严格(TrueTime 是正确性的前提)
运维复杂度低(无 leader,节点随便挂)高(GPS/原子钟对时,leader 选举)

这两个系统的选择就是分布式存储系统的根本权衡⁠——任何时候在做一个存储选型,本质上是在这张表的两列之间找位置。

参考

  • Dynamo 论文⁠: "Dynamo: Amazon's Highly Available Key-value Store"(DeCandia 2007)——所有 leaderless 系统的源头
  • Spanner 论文⁠: "Spanner: Google's Globally-Distributed Database"(Corbett 2012)——TrueTime + 外部一致性

Keywords: Dynamo, Spanner, leaderless, leader-based, consistent hashing, virtual nodes, gossip, quorum, Merkle tree, vector clock, eventual consistency, TrueTime, external consistency, Paxos, 2PC, GPS + atomic clock, commit wait, ε, always writeable, timestamp ordering