4.3 从 HDFS 到 Spanner:工作负载决定架构
HDFS 与 Spanner 都跨机器保存数据,但服务目标不同:前者面向大文件和高吞吐顺序访问,后者面向全球分片、同步复制,并在默认的可串行化隔离下提供外部一致事务。不能用产品规模替代工作负载分析。
HDFS 分离元数据与数据路径
NameNode 维护命名空间、文件到 block 的映射和副本状态;DataNode 保存实际 block。客户端先查询元数据,再直接与 DataNode 传输数据。下图表示使用副本的文件写入管线;读通常选择一个合适副本,出错后重试其他副本,不会把每次读都沿整条管线传一遍。
Client ──metadata──> NameNode
│
└──block stream──> DataNode A → DataNode B → DataNode C大 block 减少元数据数量和寻址开销,流水线复制提高写入吞吐。机架感知副本放置在网络成本与故障域之间权衡。
HDFS 适合大文件、批处理和顺序流式读写。单个文件通常只有一个写者,可追加而非随意原地修改;小文件过多又会消耗 NameNode 的文件和 block 元数据内存。它不适合低延迟随机更新,也不提供完整通用 POSIX 语义。纠删码文件的数据布局与这里的副本管线不同。具体 block 大小和副本数由部署配置决定,不应把某个默认值写成协议定律。
元数据高可用仍需要协调
Active/Standby NameNode 需要共享编辑日志或一致的日志机制、故障检测与 fencing。以 Quorum Journal Manager 为例,Active 把编辑记录写入 JournalNode 多数派;Standby 追赶这些记录后才能安全接管。配置自动故障转移时,ZKFC 通过 ZooKeeper 协调选主。检测到 Active 不健康还不够,fencing 必须阻止旧实例继续提供不安全的服务,不能让“我还活着”成为两个 Active 的共同意见。
DataNode 定期上报 block 状态,NameNode 对缺副本执行补复制,对过量副本安排删除,并考虑故障域放置。恢复流量要限速,避免节点故障触发的复制风暴影响前台作业。
Spanner 把分片、副本与时间协议组合
Spanner 将数据按 key range 分片,复制组通过共识确认写入;各副本不必在每个瞬间完全追平。事务层协调跨分片读写,TrueTime 暴露物理时间的不确定区间,与 MVCC 等机制共同支持外部一致性和历史时间读取。
关键点不是“有 GPS/原子钟所以数据库很快”,而是:
- 复制组用共识决定写入顺序;
- 事务需要并发控制和跨分片协调;
- 时间 API 把误差作为区间暴露;
- 协议通过等待等方式使提交时间戳尊重真实时间顺序。
例如提交时间戳选为 s,commit wait 要等到 TrueTime 能确认真实时间已超过 s,再让事务对外完成。单纯把机器时钟调准,不能替代这个顺序保证;跨分片事务还要处理多个参与组的原子提交。
这些保证要支付相应的网络、共识和提交等待成本。schema 与 key 设计仍要避免热点范围。
选择存储先写访问模式
| 需求 | 倾向 |
|---|---|
| PB 级大文件顺序扫描、批作业 | 分布式文件/对象存储 |
| 高写吞吐、按分区键访问、可调一致 | Leaderless 宽列/键值系统 |
| 全球事务、关系查询、外部一致 | 分布式 SQL,接受协调成本 |
| 小规模强一致元数据 | 共识 KV,不存大对象 |
还要明确:对象大小、请求率、范围查询、事务范围、读新鲜度、地域、RPO/RTO、成本和运维能力。
备份与副本不同
副本会同步应用误删除、错误写入和应用 bug。备份需要独立故障域、保留周期、不可变性和恢复演练。
对任何存储系统都要证明:
- 能恢复到哪个时间点;
- 恢复多久;
- 加密密钥和 schema 是否一起可恢复;
- 恢复环境如何避免覆盖生产;
- 最近一次完整演练是什么时候。
参考资料
- Apache Hadoop, HDFS Architecture
- Apache Hadoop, HDFS High Availability Using QJM
- Google Cloud, TrueTime and external consistency
- Google Cloud, Strict serializability and external consistency in Spanner