分库分表
When to Invoke
单机归档/分区/索引之后,容量或写入仍明显不够;或已有分片要改。用户点名分库分表时也读本文件,但默认结论可以是「现在不上」。
When NOT
慢 SQL → mysql-index。表结构/归档/分区 → table-design。面试背「分库分表」不等于业务现在要做。
风险(面试考点)
拆了之后:跨库 join、分布式事务、扩容迁数、全局唯一 ID、分页排序都变难。选错分片键(按状态、按日期当唯一键)会导致热点或查一次扫全分片。
分区表 ≠ 分库分表。分区仍是一个 MySQL 实例。
适用场景
- 订单按
user_id查询占 90%,单库写入顶不住 - 流水按
merchant_id查询,单表归档后仍过大
方案选型(轻量优先)
| 优先级 | 做法 | 条件 |
|---|---|---|
| 0 | 不上分片 | 默认 |
| 1 | 归档 + 分区 + 正确索引 | 先做完再评估 |
| 2 | 单库分表(同实例) | 单表过大但实例 IO 还行 |
| 3 | 分库分表 | 实例容量/连接/写入都不行 |
升级触发(要能说出数字):单表 > 千万且归档无效,或写入打满单实例磁盘/CPU,且查询有稳定等值键。
默认方案(真要拆时)
分片键 = 最常用等值查询的那个列,且写入分散:C 端订单用 user_id,B 端商户后台若也是主入口,考虑订单表冗余或映射表,不要幻想一个键同时完美服务两端。
shard = user_id % 16 # 简单取模,扩容要迁移
# 或一致性哈希:扩容迁移更少,运维更复杂
规则:
- 所有查询尽量带分片键
- 禁止跨分片 join;应用侧聚合或宽表
- ID 用雪花(
distributed-id),按分片键路由不靠自增 - 跨分片分页:禁止简单
LIMIT合成;用 ES/宽表/业务限制必须带 user_id - 跨分片写:不要上 Seata 当默认,见
distributed-transaction
订单号可内嵌分片信息,便于客服按单号路由。
反例
错误:一上来 8 库 64 表,列表还要「全平台按时间排序」。 正确:这种查询形态不适合分片;先改查询或上检索。
错误:用 status 当分片键。
正确:状态基数低,热点严重,且状态会变(迁移噩梦)。
错误:把「加个联合索引」写进分片方案。
正确:回 mysql-index。
验证
- 主路径 SQL 都带分片键,能路由到单片。
- 扩容/迁移有演练,不是只写了算法。
- 不存在隐藏全分片扫描的管理接口打生产。
评审清单
- 是否其实只用索引/归档就够
- 分片键是等值高频列且不可变
- 跨片 join/排序有交代
- 没有和索引 skill 混写