Skip to content

分库分表

垂直/水平拆分、分片键选择、扩容、分布式 ID、跨库查询。

Updated View as Markdown
For humans

分库分表

分库分表是单库容量和写入吞吐到极限后的方案,代价巨大。开篇先说判断标准:能用索引和分区表解决的,不要上分库分表。单表数据量过 2000 万或写入 QPS 过 5000 再考虑,且业务能接受跨库查询变慢。

垂直与水平拆分

垂直拆分 vs 水平拆分

  • 垂直拆分:按业务域拆库(订单、用户、商品分开),或拆大表字段(热点字段和非热点字段分开)。解决单库连接数和 IO 压力,不解决单表数据量
  • 水平拆分:同一张表按分片键拆成多张表。解决单表数据量和写入吞吐,代价最大

水平拆分是面试重点。

分片键与分片算法

分片键选择是影响最深远的决策,三个标准:高基数(用户 ID 优于性别)、写均匀(避免单调递增 ID 全写一个分片)、匹配查询模式(按用户 ID 分片,同一用户的订单在同片,查历史不跨片)。

分片算法:

算法 优点 缺点
取模(ID % N) 分布均匀、实现简单 扩容要迁移大部分数据
范围分片 范围查询友好 热点倾斜(新数据集中)
一致性哈希 扩容只迁移相邻节点数据 实现复杂

扩容是分库分表最痛的环节。取模扩容 N 变 N’ 时大部分数据要重算位置迁移。两个解法:按 2 的幂规划容量(双倍扩容只迁移一半数据);双写迁移(新库作为从库同步,追平后切换,不停服)。

分布式 ID

分库分表后数据库自增失效(多个分片各自自增会重复)。主流方案:

方案 特点 适用
UUID 本地生成,无序、存储大、页分裂 对顺序无要求
雪花算法 趋势递增、高性能、无依赖 通用首选,注意时钟回拨
号段模式(Leaf-segment) 全局严格递增、双号段缓冲 对账、订单号连续
Redis INCR 简单,依赖 Redis 高可用 计数器类

选型:通用业务用雪花,对账要求连续用号段模式。雪花算法的 64 位结构(时间戳 + 机器 ID + 序列号)和时钟回拨处理是必背细节。

跨库查询的四大难题

  1. 跨库 JOIN:方案是全局表(小表复制到每个分片)、冗余字段、ER 分片(强关联表按同键分片保证同库)
  2. 非分片键查询:映射表(记录非分片键到分片键的关系)、前缀分片(把用户特征嵌进订单 ID)、同步到 ES 做检索
  3. 深分页:多分片各取前 N 再合并,offset 越大越慢。带上分片键查询或业务上规避
  4. 分布式事务:见分布式事务篇

面试追问

  1. 什么时候该分库分表? 单表超 2000 万、写入 QPS 超 5000、索引和分区表已到极限。能用分区表解决的别上
  2. 分片键怎么选? 高基数、写均匀、匹配查询模式。按用户 ID 分片让同一用户数据同片
  3. 取模分片扩容怎么办? 取模扩容大部分数据要迁移。按 2 的幂规划(双倍扩容只迁一半)或双写不停服迁移
  4. 分布式 ID 怎么选? 通用用雪花(趋势递增、无依赖、防时钟回拨),对账连续用号段模式,无所谓顺序用 UUID
  5. 跨库 JOIN 怎么解决? 全局表、冗余字段、ER 分片。业务层两次查询替代 JOIN 也是常见做法
Navigation

Type to search…

↑↓ navigate↵ selectEsc close