跳转到内容

复制与分片

单机到头了怎么办?复制(多副本,扩展读与容错)与分片(拆数据,扩展写与容量)——每个"扩展"都以一致性或复杂性为代价。本章讲主从复制、多主复制、分片键选择与跨分片事务(→ 复制与一致性模型)。

一主多从:主处理写、复制日志,从处理读——扩展读与容错(主挂了提升一个从)。

异步复制(默认)下从库落后主库——复制延迟导致"读到旧数据":

  • 读己之写(read-your-writes)被破坏:用户刚发的帖子刷新看不到——对策:关键读走主库、会话粘住主库(sticky session);
  • 单调读被破坏:刷新一次看到新数据、下一次又看到旧数据(落到落后的从库)——对策:会话粘住同一个从库。

复制延迟是异步复制的固有代价,不是 bug——同步复制消延迟但每写等全部副本(主挂了就不能写,可用性换一致性,→ 故障模型与 CAP)。

主挂了提升从为主——**脑裂(split-brain)**风险:旧主复活时两个主同时写。对策:多数派确认(新主要得到多数副本投票)、fencing token(→ 协调服务与实践模式)。自动故障切换的正确性靠共识协议(Raft 组),手动切换靠流程。

多个主都能写(跨地域部署的延迟需求),冲突由此而来:

  • 写冲突:两个主同时改同一行——需要冲突解决(时间戳最后写胜、CRDT、应用层合并);
  • **最后写胜(LWW)**丢弃并发写之一——语义上"丢失更新";
  • MySQL Group Replication、CockroachDB、Cassandra(多主风格)各有取舍。

多主的真正用武之地是跨地域写:每个机房本地写(低延迟),后台复制——以冲突解决为代价换本地延迟。

数据按分片键拆到多台机器——扩展写与容量:

分片键选错几乎无法事后修复(迁数据是全表级的痛苦)。判断标准:

  • 查询模式:查询都带分片键(如 user_id)→ 点查落到单分片,高效;不带 → 广播所有分片(scatter/gather);
  • 热点:分片键分布均匀(自增 ID 均匀但可能泄露顺序;hash 均匀)——热点(大 V 用户、单一租户)压垮单分片;
  • 范围 vs 哈希:范围分片支持范围查询但易热点,哈希分片均匀但不支持范围——混合(时间 + 哈希)是常见折中。

跨分片的事务需要两阶段提交(2PC):协调者先问所有参与者"能提交吗"(prepare),全 yes 再提交(commit)。

  • 2PC 的代价:协调者是单点——prepare 后协调者挂了,参与者锁着资源等(阻塞);网络分区时可能不一致;
  • 2PC 的可用性代价:一个参与者不可用,事务就卡住——跨分片事务的扩展性差是分布式数据库(NewSQL:Spanner、CockroachDB、TiDB)的核心难题,它们的解法是 Paxos 组 + 分布式 2PC;
  • 能避免就避免:按分片键设计事务(一个用户的数据在一个分片)是应用层的第一原则。
手段扩展什么代价
主从复制读、容错复制延迟(读到旧数据)
多主复制本地写延迟冲突解决
同步复制一致性写延迟、可用性
分片写、容量广播查询、跨分片事务(2PC)
2PC跨分片原子性协调者单点、阻塞
  • 复制延迟导致"读到旧数据":异步复制的固有代价;关键读走主库
  • 脑裂靠多数派 + fencing token 防御
  • 多主以冲突解决为代价换本地写延迟;LWW 丢弃并发写
  • 分片键选错几乎无法事后修复:查询模式、热点、范围 vs 哈希
  • 2PC 的协调者是单点:跨分片事务能避免就避免(按分片键设计)