数据库分库分表的 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. 双写阶段:新旧库同时写入(数据同步)
  2. 校验阶段:对比新旧库数据一致性
  3. 灰度切流:1% → 10% → 50% → 100% 流量逐步切到新库
  4. 回滚准备:任何阶段出问题可秒级回滚

四、3 个常见坑

坑 1:分片键选错

真实案例:某电商用"商品状态"做分片键,结果 80% 售罄商品集中在一个分片,热点分片性能反而下降

坑 2:分库分表后想做 JOIN

真实案例:分库后还用 JOIN,查询从 100ms 变成 30 秒,业务崩溃。

坑 3:扩容时数据迁移失败

真实案例:直接用脚本迁移 10 亿数据,迁移到一半中断,新旧数据不一致,业务停滞 12 小时。

联系方式:400-025-0992

官网https://www.wanmi.com