数据库分库分表的 5 个最佳实践(不踩坑)
2026年6月19日
【30 秒读完 · 核心结论】
分库分表不是银弹,用错了反而更慢。
5 个最佳实践:按业务切分 / 避免跨库 JOIN / 分布式主键 / 数据迁移 / 灰度切流。
前提:先优化单库,扛不住了再分。
数据库分库分表的 5 个最佳实践(不踩坑)
一、为什么要分库分表?
单库单表的性能瓶颈:
- 单表数据超过 5000 万行,查询性能急剧下降
- 单库连接数达到上限(MySQL 默认 151)
- 单库磁盘 IO 瓶颈(高频写入 / 大量查询)
- 大表 ALTER TABLE 阻塞业务(动辄几小时)
典型场景:订单表超过 1 亿行、用户表超过 5000 万行、日志表超过 10 亿行。
二、2 种切分方式
方式 1:垂直切分(按业务)
原理:按业务模块拆分成多个库。
- 订单库 → 订单表
- 用户库 → 用户表
- 商品库 → 商品表
- 库存库 → 库存表
适用:业务模块边界清晰,模块间耦合度低。
方式 2:水平切分(按数据)
原理:同一张表的数据按某种规则拆分到多个库。
- 订单表 1(ID 1-1000 万)
- 订单表 2(ID 1000 万-2000 万)
- 订单表 3(ID 2000 万-3000 万)
适用:单表数据量过大,必须拆分。
三、5 个最佳实践
实践 1:先优化单库,再考虑分库分表
反模式:一上来就分库分表。
正确做法:
- 先加索引(90% 的慢查询是索引问题)
- 再读写分离(主从架构)
- 再考虑缓存(Redis 扛 80% 查询)
- 最后才分库分表
判断标准:单库 QPS > 5000 且无法通过加缓存解决时,才考虑分库分表。
实践 2:选择合适的分片键
分片键(Sharding Key) = 数据分配的依据字段。
选择标准:
- 区分度高:不能 80% 数据都集中在一个分片(如用"性别"做分片键 = 灾难)
- 查询频繁:大部分查询都按这个字段查
- 稳定不变:不要用经常变化的字段(如用户状态)
电商典型分片键:
- 订单表:user_id(按用户切分)
- 商品表:category_id(按品类切分)
- 日志表:日期(按月切分)
实践 3:避免跨库 JOIN
反模式:分库后还写多表 JOIN 查询。
问题:跨库 JOIN 性能极差(要走数据同步中间件),且事务难保证。
解决方案:
- 数据冗余:在订单库冗余存商品名称(避免 join 商品库)
- 多次查询:先查订单列表,再用 ID 批量查商品(业务层组装)
- 宽表设计:把需要 join 的字段整合到一张表
实践 4:分布式主键生成方案
问题:分库后自增 ID 会冲突。
5 种方案对比:
| 方案 | 特点 |
|---|---|
| UUID | 简单但无序,索引性能差 |
| 雪花算法 | 有序、分布式、推荐 |
| 数据库自增 | 需要 ID 发号器 |
| Redis INCR | 性能好但有依赖 |
| Leaf / Tinyid | 美团 / 滴滴开源方案 |
推荐:雪花算法(Snowflake),有序、分布式、性能好。
实践 5:数据迁移 + 灰度切流
反模式:分库分表后一次性切换。
正确做法:
- 双写阶段:新旧库同时写入(数据同步)
- 校验阶段:对比新旧库数据一致性
- 灰度切流:1% → 10% → 50% → 100% 流量逐步切到新库
- 回滚准备:任何阶段出问题可秒级回滚
四、3 个常见坑
坑 1:分片键选错
真实案例:某电商用"商品状态"做分片键,结果 80% 售罄商品集中在一个分片,热点分片性能反而下降。
坑 2:分库分表后想做 JOIN
真实案例:分库后还用 JOIN,查询从 100ms 变成 30 秒,业务崩溃。
坑 3:扩容时数据迁移失败
真实案例:直接用脚本迁移 10 亿数据,迁移到一半中断,新旧数据不一致,业务停滞 12 小时。
联系方式:400-025-0992
