跳到内容

第 5 卷 / 第 16 章 / 第 1 课

16.1 分片、复制组与共识 ​

本节讨论常见的自动分片数据库:把键空间切成数据单元,为每个单元放置副本,再用路由与共识组织访问。分布式数据库也可以先复制而不分片,不能把这一条实现路线当成全部定义。

先确定为什么要分布式 ​

常见目标有四类,方案并不相同:

  • 容量:单机存储、内存或 I/O 已不足;
  • 吞吐:可按互不冲突的键并行扩展;
  • 可用性:节点或故障域损坏后仍能服务;
  • 地理延迟:让数据与主要用户或计算靠近。

仅为了高可用,单主库加同步/异步副本可能比自动分片数据库简单。仅为了分析容量,对象存储加计算集群也可能更合适。分布式 SQL 的价值在于需要跨分片事务、关系模型与自动再平衡同时成立时,愿意承担相应复杂度。

不要引用“单机只能承载几 TB”一类固定门槛。上限取决于硬件、数据温度、索引放大、维护窗口、恢复时间和 workload;真正的分片触发点应来自容量曲线与 SLO。

分片函数决定局部性与热点 ​

Range Sharding ​

排序键空间被切成连续区间:

text
[a, g)  -> shard 1
[g, n)  -> shard 2
[n, z)  -> shard 3

范围查询只访问相邻分片,数据压缩和顺序扫描也更自然。但单调递增时间或 ID 可能把最新写集中到最后一个 range。系统可自动 split 和迁移 range,却不能消除单个逻辑键的热点。

Hash Sharding ​

对分区键哈希后映射到 bucket,通常能分散随机键写入:

text
bucket = hash(partition_key) mod bucket_count

它牺牲相邻键局部性。直接对物理节点数取模还会让扩缩容时大量键重新映射,因此生产系统通常引入固定逻辑 bucket、range/slot 或一致性哈希层,将逻辑分片再映射到节点。

复合键 ​

实际常组合租户、时间 bucket 和实体 ID:

text
tenant_id / month / event_id

租户字段便于聚集同一租户的数据,权限隔离仍由访问控制完成;月份限制时间跨度,并不保证数据量上限,event ID 需在选定作用域内保持唯一。但大租户仍需进一步打散,小租户过度打散又会增加 fan-out。分片键是长期数据契约,不是上线前随便选一个高基数字段。

分片与副本是两条不同的轴 ​

分片回答“哪部分数据由谁负责”,复制回答“这部分数据有几份、如何保持同序”。一个常见结构是:

text
range A -> replicas A1, A2, A3 -> one Raft group
range B -> replicas B1, B2, B3 -> another Raft group

每个 range 通常有自己的 leader/leaseholder 和共识日志。客户端连到任意网关,并不表示任意副本都能无条件返回最新读;网关可能转发到当前负责人,陈旧读则可能在满足 closed timestamp 等条件时由 follower 本地服务。

Raft 解决复制日志顺序,不解决全部数据库问题 ​

Raft 把客户端命令追加到复制日志。leader 可以在当前任期的一条日志被多数投票成员持久化后,推进其已提交位置并连带提交之前的前缀;不能仅凭旧任期条目已有多数副本就直接提交它,状态机按同一日志顺序应用命令。三投票副本需要两票,五副本需要三票;增加非投票副本不会自动增加容错投票数。任期、选举限制和日志匹配共同保证已提交前缀不会被另一合法 leader 改写。

它主要解决一个复制组的共识问题,不自动提供:

  • 跨多个 Raft group 的原子事务;
  • SQL 隔离级别与谓词冲突检测;
  • 分片键选择、热点拆分和全局二级索引;
  • 备份、误删恢复和跨版本升级;
  • “写到多数派就一定不会丢”的硬件持久化细节。

数据库需要在 Raft 之上增加事务记录、锁/intent、MVCC 时间戳和恢复协议。

Leader、Lease 与读路径 ​

写入通常经过当前 leader,至少等待多数投票副本确认。线性一致读需要确认当前读权限,并等待本地状态机应用到相应已提交位置;可由 ReadIndex 或有完整时钟前提的租约协议完成。仅记得“我曾是 leader”不够。例如确认可读到日志位置 105,而本地只应用到 102,就不能拿 102 的状态直接回答这次读。

Follower read 降低跨地域读延迟,但必须明确时间戳:

  • 线性一致最新读可能跨区找负责人;
  • 历史读可在安全时间与本地已应用进度允许的范围内读取 MVCC 版本;要称为有界陈旧,还需确认请求时间戳满足业务给定的新鲜度下限;
  • 普通异步副本读若没有边界,只能描述为“可能陈旧”。

“读写分离”不是免费的扩容开关。业务必须知道哪些页面可以旧、允许旧多久,以及写后从哪里读。

再平衡的隐藏成本 ​

节点加入后,系统需要复制 range 快照、追赶日志、转移 leader/lease,并更新路由元数据。再平衡会消耗网络、磁盘和 CPU;若多个副本同时迁移,还可能削弱故障容忍。

运维至少应监控:

  • range 大小和 QPS 偏斜;
  • leader/lease 分布;
  • 副本欠复制与日志落后;
  • split、merge、snapshot 和 rebalance 流量;
  • 单键热点与跨分片 fan-out;
  • 多数派丢失时哪些 range 不可用。

参考 ​

下一节处理比复制更难的问题:一个事务同时触及多个分片时,怎样同时保证原子提交与可串行化。

Built with VitePress | Software Systems Atlas