Skip to content

08 · 分库分表与读写分离 ​

分库分表是"最后的手段",不是"涨姿势就上"。本篇讲清它到底解决什么问题、读写分离与水平拆分怎么选、路由怎么算、以及代价清单——让"要不要分"变成可判断的问题。

速览卡 ​

项内容
一句话定位数据库扩展三板斧:读写分离、垂直拆分、水平拆分,以及各自的代价
核心内容什么时候该拆、路由算法、分布式 ID、拆分后失去什么
读完能回答我的库该先做读写分离还是直接分表?分表后 JOIN/事务怎么办?
相关板块DB02 索引 | DB03 事务 | C04 一致性与事务

一、扩展顺序:先把便宜的做完 ​

text
① 索引与 SQL 优化(DB02)      —— 几乎免费
② 读写分离(加从库)            —— 扩读
③ 垂直拆分(按业务分库)        —— 拆耦
④ 水平分表(按数据分片)        —— 扩写

重要认知:大多数系统止步于 ①~③。水平分表是最重的手术,前提是前面的手段都已用尽。


二、读写分离:加从库分担读 ​

text
写 → 主库
读 → 从库(主从复制,可能有延迟)
收益代价
读水平扩展主从延迟:刚写就读到旧数据
主库专注写要处理"写后立即读"一致性

主从延迟的经典坑:下单成功后立刻查订单,查的是从库旧数据 → 前端以为下单失败。 对策:关键"写后读"走主库;可容忍延迟的读走从库。


三、水平分表:按什么分 ​

拆分维度路由键例子适合
取模/哈希主键或业务 iduser_id % 16单点查询(按 id)
范围时间/区间order_2024、order_2025时间序列、归档
一致性哈希任意键按 key 哈希环节点增减影响小

判断框架:先确认查询模式——大多数查询按什么键来?那个键就是分片键。 分片键错了,所有查询都要扫全部分片,等于没分。


四、分片后的三个"失去" ​

失去后果对策
跨分片 JOIN不能在 SQL 里关联应用层聚合 / 冗余宽表 / 按分片键聚合
跨分片事务本地事务不跨分片分布式事务(见 C04),成本高,尽量规避
全局唯一主键自增在每个分片会撞分布式 ID(见下)
模糊查询/范围跨片要广播到所有分片搜索用 ES(见 DB09),热点用缓存

分库分表后的查询要带着分片键才快;不带分片键的查询是"广播查询",代价随分片数线性增长。


五、分布式 ID:全局唯一还不撞 ​

方案特点
雪花算法(Snowflake)时间戳 + 机器位 + 序列号,趋势递增,常用默认
UUID简单但无序、太长(作主键伤索引)
数据库分段发号每次拿一段号,简单可靠

为什么不能自增:多分片各自自增会撞;用 UUID 当主键会因无序导致 B+树页分裂、索引膨胀。雪花号"趋势递增"对聚簇索引最友好。


六、什么时候才真正需要水平分表 ​

判断信号(多个同时出现才考虑):

  • 单表数据量大到索引装不进内存、写入/查询延迟明显劣化
  • 已经做过 SQL 优化、读写分离、垂直拆分,仍不够
  • 查询模式能稳定给出分片键
  • 团队能承担中间件/路由的运维成本

反信号:数据量没到千万级、查询没固定键、团队还没啃完索引——先回去做 DB01~DB07。


七、自检清单 ​

  • [ ] 知道扩展顺序:先索引,再读写分离,再垂直,最后水平
  • [ ] 知道主从延迟与"写后读"对策
  • [ ] 分片键按查询模式定,绝大多数查询带分片键
  • [ ] 知道分片后 JOIN/事务/全局 ID 的代价与对策
  • [ ] 分布式 ID 选雪花,不裸用自增/UUID
  • [ ] 知道水平分表是最后手段,有明确的触发信号

八、常见误区 ​

误区事实
"分表能解决所有慢查询"没分片键的查询变广播,更慢
"自增主键到分片会撞"要全局唯一,用分布式 ID
"UUID 作主键没问题"无序导致索引膨胀,聚簇不友好
"读写分离没代价"主从延迟会让"写后读"读到旧数据
"分完就完事"应用代码、迁移、运维都要跟着改造
"先上分表再说"没触发信号就分,纯加成本

九、动手练习 ​

练习 A:选分片键 ​

订单表要按用户查、按商家查、按时间查。你会怎么分片?说明取舍,以及"另一个维度"怎么查。

练习 B:设计分布式 ID ​

用雪花算法的三段结构(时间/机器/序列)设计一个 64 位 ID,并说出各占多少位、能支持多少并发。

练习 C:评审一次"该不该分" ​

某表 500 万行、查询都带 user_id、已做读写分离、写压力仍高。给出你的升级路径与理由。


相关文档 ​

本区 ​

其他域 ​