Skip to content

元数据卡

  • 前置知识:Vol 5 数据库(B+ 树、LSM-Tree)、第3章(分布式共识)
  • 预计时间:45 分钟
  • 核心难度:进阶
  • 阅读模式:高度专注
  • 完成标志:能说清一致性哈希如何应对节点变化,理解 HDFS 的分块+副本设计,掌握 Cassandra 的 Ring 和读写路径,知道 Spanner 用了哪些技术实现全球级分布式数据库

你的进度

3 个节点的 etcd 集群已经能稳定地用共识协议管理配置和锁了。但战报系统产生的日志数据越来越多——侦查报告每天 5GB,指挥部每天会商记录 3GB,加上各堡垒的兵力调动记录。这些数据不能塞进 etcd(它只适合小数据量、强一致性的元数据)。

林将军说:"数据是粮草。粮草不能全堆在一个仓库里,一烧就全没了。分开放,放多远?怎么找?"

你面对的是分布式存储的核心问题:海量数据放在多台机器上,怎么分,怎么找,怎么保证可靠。


你的任务

掌握分布式存储的三种核心范式:一致性哈希(确定数据在哪台机器上)、HDFS(大文件的块存储)、Cassandra(列式存储 + 分区 + 最终一致性)。最后用 Google Spanner 作为例子,看看这些技术的综合应用能达到什么高度。


破局 · 溯源


不能只靠"取模"

最简单的数据分片是取模:数据 key 的 hash 值对节点数 N 取模,决定数据去哪台机器。

node = hash(key) % N

问题:当你增加一台机器(N -> N+1)或减少一台机器时,几乎所有的 key 都会迁移到新节点。对于 TB 级数据,这意味着灾难性的数据搬运。

更糟糕:你不能优雅地扩容。加一台机器意味着整个集群要重新平衡。

你需要一种方案:节点增删时,尽量少的数据需要迁移。这就是一致性哈希(Consistent Hashing)。


一致性哈希

一致性哈希的思路出奇简单:把哈希值空间看作一个环,范围从 0 到 2^32 - 1。每个节点在这个环上有一个位置(对 node ID 取 hash)。每个 key 也在这个环上有一个位置,然后顺时针找到第一个节点。

        节点 A (hash = 100)
            |
            |
key X ----> |  ← key X 顺时针找到的第一个节点是 A
            |
            |
   节点 C   |    节点 B
   (900)   |    (400)
            |
            |
        key Y (hash=700) → 顺时针找到节点 C

当加入一个新节点 D(hash=350)时,只有 D 到顺时针下一个节点之间的 key 需要迁移到 D。也就是说,只有 [350, 400) 这一段的数据从 B 搬到了 D——其他 key 不受影响。

虚拟节点优化

一致性哈希有一个缺陷:当节点数量少时(3、5、7 台),哈希在环上的分布不均匀。引入虚拟节点:每台物理节点在环上占有多个位置(例如 100 个虚节点),让每个物理节点负责的环段数量趋于均匀。

Cassandra 使用了这个技术——每个物理节点默认拥有 256 个 token(虚拟节点),大幅改善数据分布的均匀程度。

| 物理节点 | 物理节点数量 | 无虚节点时的数据倾斜 | 256 虚节点时 |
|---------|-------------|-------------------|-------------|
|    3    |    3        |   ~45% 差异       |   <3% 差异  |
|   10    |   10        |   ~20% 差异       |   <1% 差异  |

深入:HDFS —— 大文件往哪放

HDFS(Hadoop Distributed File System)是分布式文件系统的经典设计。它解决的核心问题:单个文件大到单机放不下(TB 级)时,怎么存储和访问?

基本架构:

+-------------+     +-------------+     +-------------+
|  NameNode   |     |  NameNode   |     |  NameNode   |
|  (Active)   |<----|  (Standby)  |     |  (Standby)  |
+------+------+     +-------------+     +-------------+
       |
       | 元数据操作(文件列表、块位置)
       |
+------+---------+---------+---------+
|      |         |         |         |
v      v         v         v         v
+---+  +---+    +---+    +---+    +---+
|DN1|  |DN2|    |DN3|    |DN4|    |DN5|
+---+  +---+    +---+    +---+    +---+
|blk1 |blk1_rep|blk2 |blk2_rep|blk3 |
|blk2 |blk3_rep|blk4 |blk5    |blk4_rep
+-----+--------+-----+--------+-----+
  • NameNode:管理文件系统元数据。文件名→块列表、每块的副本位置。NameNode 是 HDFS 的单点故障(虽然有 Active/Standby 方案)。
  • DataNode:实际存储数据的节点。数据以块(Block)为单位存储,默认 128MB。
  • 每个文件被切分为若干块,每个块在多个 DataNode 上保存副本(默认 3 副本)。

读流程:

客户端: "读 /reports/daily-2026-06-24"
    |
    v
NameNode: 文件有 8 个块
    块 1: DN1, DN3, DN5
    块 2: DN2, DN4, DN6
    ...
    |
    v
客户端: 从离自己最近的 DataNode(网络拓扑最近)读取每个块
    本地 DN → 同机架 DN → 跨机架 DN

写流程:

客户端: "写 /reports/daily-2026-06-24"
    |
    v
NameNode: 分配 8 个块,每个块选 3 个 DataNode
    |
    v
客户端: 数据流经过"管线式"复制
    客户端 → DN1 → DN2 → DN3
    (客户端写完一个 packet 后,再写下一个 packet,
     不再等前一个被确认的 ACK)

HDFS 适合顺序读写大文件,不适合随机写入和低延迟访问。它是 MapReduce 的底层存储,但不是通用目的文件系统。


Cassandra:写优化的分布式数据库

2019 年,Netflix 运行着超过 2,000 个 Cassandra 节点集群,每个集群承载 数 PB 数据。Cassandra 是分布式存储中"AP 派"的代表——面向写优化、最终一致性、无单点故障。

数据分布:Ring + Partition

Cassandra 的数据分布基于一致性哈希(虚节点模式)。每行数据通过 Partition Key 决定落在哪个节点上。

Cassandra Ring(简化):

+----+     +----+     +----+     +----+     +----+
| N1 |     | N2 |     | N3 |     | N4 |     | N5 |
|t=1 |     |t=2 |     |t=4 |     |t=7 |     |t=9 |
+----+     +----+     +----+     +----+     +----+
                                            
key "user_1001" hash=3  → 落在 N3 (范围 2-4)
key "user_2002" hash=5  → 落在 N4 (范围 4-7)

写路径:

客户端写 key="user_1001" value={name:"张",score:95}
    |
    v
协调器(Coordinator, 可以是任何节点)
    |--- 计算 key 的 token → N3
    |--- 根据一致性级别(QUORUM = 大多数)
    |--- 同时写入 N3, N4, N5(三个副本)
    |--- 写入 MemTable(内存表)+ CommitLog(追加写)
    v
当大多数节点确认写入后,返回客户端成功

读路径:

客户端读 key="user_1001"
    |
    v
协调器:
    |--- 向 N3, N4, N5 发送读请求
    |--- 带读修复(read repair):检查数据版本
    |--- 返回最新版本的数据给客户端
    v
如果副本间存在数据不一致,后台进行修复

Cassandra 的写性能极高(追加写,CommitLog 是顺序 IO),适合写入密集场景。它的最终一致性模型意味着并发读写可能出现不一致——业务层需要处理这种可能性。

Cassandra 分区与二级索引

Cassandra 的查询模式极度依赖主键(Partition Key + Clustering Columns)。没有 JOIN,不能用非主键列做范围扫描——这些限制迫使你在建表阶段就设计好查询模式。

这是 Cassandra 与关系型数据库最本质的区别:查询驱动表设计,而不是表驱动查询设计。


Spanner:Google 的全球级数据库

Spanner 不是一个新的存储引擎,它是多种技术的综合:一致性哈希(分片)、Paxos(副本内共识)、TrueTime(全局时间戳)、以及 SQL 层的分布式查询优化。

Spanner 架构(概念):

        SQL 层(F1)
            |
    +-------+-------+
    |               |
分布层(分片 + 路由)
    |               |
+---+---+      +---+---+
| Paxos |      | Paxos |  每个分片是一个 Paxos 组
| 副本  |      | 副本  |
| Z1 Z2 Z3     | Z4 Z5 Z6
+-------+      +-------+

Spanner 的核心突破:

  1. 外部一致性(External Consistency):通过 TrueTime 保证事务的提交时间戳与真实时间对齐。事务 T1 在 T2 之前提交,T2 一定能看到 T1 的结果。

  2. 读写事务:对跨分片事务,Spanner 使用两阶段提交(2PC),协调者通过 Paxos 保证高可用。

  3. 无锁只读事务:利用 TrueTime 实现快照隔离,读操作不阻塞写操作。

Spanner 证明了:分布式共识(Paxos)+ 全局精准时钟(TrueTime)+ 两阶段提交(2PC)的组合可以在全球范围内实现外部一致性。代价是毫秒级的事务延迟——但在全球部署的场景下,这是可接受的。


对比:三种范式

维度HDFSCassandraSpanner
模型文件系统宽列存储关系型 + SQL
一致性强一致(单 NameNode)最终一致(可调)外部一致性
分片块(固定 128MB)一致性哈希(Token)目录分片(用户自定义)
写方式追加 + pipeline追加 MemTable + CommitLog追加 + Paxos 确认
容错多副本 + NameNode HAGossip + hinted handoffPaxos + 分片迁移
适用OLAP / 批处理写入密集 / 时序 / IoT全球 OLTP / 金融
单机存储文件系统LSM-TreeColossus (GFS 升级版)

常见陷阱

  1. HDFS NameNode 是瓶颈但无法绕开。 所有文件元数据操作走 NameNode,100 万文件的元数据存储在几十 GB 内存中。超过 1 亿文件,NameNode 内存成为瓶颈。HDFS Federation 可以缓解但没有根治。

  2. Cassandra 的查询只能预先设计。 你无法在 Cassandra 上写"where age > 30 and name like '%张%'"这样的灵活查询。次级索引性能差到不建议使用。建表前想清楚所有查询路径。

  3. 一致性哈希的"均衡"不是自动的。 节点加入/退出后,数据迁移需要时间。这段时间内某些节点负担加重。"虚节点"可以改善但不消除问题。

  4. 分布式存储的"写后读自己"不保证读到。 即使在 Cassandra 的 QUORUM 级别下,如果你写入后立刻读取,可能因为 read repair 尚未完成而读取到旧值。需要使用一致性级别 ONE/QUORUM + 加时间戳版本对比。


通关挑战

  • 热身:用一致性哈希实现一个简单的 key-value 分发器。支持添加/移除节点,验证节点变化时只有少量 key 迁移。用 1000 个 key 和 5 个基础节点测试,然后增加一个节点观察迁移比例。

  • 挑战:在本地启动 Cassandra 容器,用 CQL 创建一个表模拟侦察日志存储(侦察员 ID、时间戳、坐标)。写入 10 万条记录,观察分片分布情况。

cql
CREATE TABLE scout_logs (
    scout_id text,
    recorded_at timestamp,
    lat double,
    lng double,
    report text,
    PRIMARY KEY (scout_id, recorded_at)
) WITH CLUSTERING ORDER BY (recorded_at DESC);
  • 观察:用 nodetool ring 查看 Casssandra 的 token 分布。杀死一个节点,观察 hint 和修复过程。再恢复节点,观察流式传输。

旅人笔记

分布式存储没有银弹。一致性哈希解决了"增删节点少搬家"的问题,但选择查询模式时要靠你对数据访问路径的提前设计。HDFS 是批处理的最佳搭档,Cassandra 适合写入密集的在线服务,Spanner 用极高的工程复杂度换来了全球级的外步一致性。选型时,先看清你的读写模型、一致性需求、和延迟容忍度。


下一站预告

数据存好了,分好了,副本有了。但数据不只是存储——数据需要被计算。你在 1000 台机器上存了 10PB 的日志,现在需要统计"每个前哨站每小时的警戒日志数量"。传统 SQL 在这个规模上跑不动。下一章,分布式计算上场。

Built with VitePress | Software Systems Atlas