跳转至

MySQL 分库分表:数据多了以后怎么拆

假设订单系统越来越慢,我会先看慢 SQL、索引、锁等待和磁盘,而不是一上来就拆库。如果一次查询本来就扫全表,拆成十张表后再扫十张,问题可能更复杂。

分库分表适合处理单实例容量、写入或维护窗口已经难以满足业务的情况。没有“超过两千万行必须拆”的通用门槛:行宽、索引、热点、硬件、SQL 和备份恢复时间都影响判断。

四种拆法,用订单系统理解

拆法 例子 带来的变化
垂直分库 用户、订单、商品分别放入不同数据库实例 按业务隔离资源,业务之间的关联和事务变复杂
垂直分表 订单常用字段放主表,大段备注放扩展表 常用查询读取更少数据;读取完整订单需要关联
水平分表 orders 按用户分成 orders_0~orders_3 每张表更小;若仍在同一个实例,CPU、I/O 和故障域仍共享
水平分库 一部分用户订单在实例 A,另一部分在实例 B 真正分摊机器容量与负载,同时引入跨实例运维

在同一实例创建多个 schema,并不会获得多台机器的处理能力。分库分表解决容量和负载,高可用仍需为每个分片配置复制、切换和备份。

分片键决定请求去哪里

用户通常按自己的账号查看订单,可以用 user_id 作为分片键。教学例子采用四个逻辑分片:

user_id = 1005
  → 1005 % 4 = 1
  → 分片 1
  → orders_1
SELECT order_id, status, amount
FROM orders
WHERE user_id = 1005 AND order_id = 90001;

应用或分片中间件根据 user_id 找到真实表,SQL 中的逻辑表名被映射到目标表。如果只给 order_id,又没有订单到分片的映射,可能要查询所有分片,再汇总结果。

分片规则可由应用实现,也可由分片中间件维护。代理本身也要多实例、配置一致、支持连接故障恢复;多加一层代理不会自动获得高可用。

我选分片键时主要看三个问题:常用查询是否带这个字段、数据能否分得均匀、字段是否稳定。大客户可能拥有绝大多数订单,即使用用户 ID 分片,也可能形成热点。

规则 优点 容易遇到的问题
按月份/范围 时间查询和历史归档直观 最新月份承受大部分写入,跨月查询要合并
哈希取模 通常比较均匀 直接从 %4 改成 %8 会改变大量数据的位置
逻辑分片 + 路由表 逻辑分片可逐个迁移到新实例 要维护映射版本、迁移状态和切换一致性

拆完以后,哪些事情会变难

跨库查询。 单个用户的订单很好查,“所有用户最近一个月金额排名”却需要扫描、聚合多个分片。大范围报表通常考虑同步到分析库;要接受并说明同步延迟。

分页与排序。 每个分片返回的前十条不等于全局第十页。中间件可能拉取更多记录再归并,深分页成本很高。可按业务考虑基于时间和唯一 ID 的游标分页。

事务。 同一分片可以使用本地事务。跨分片转账、扣库存涉及多个提交点,需要按业务采用分布式事务或可靠事件与补偿;重试、幂等和对账是方案的一部分。

唯一性。 每张表自增 ID 都可能从 1 开始,单表唯一约束不再代表全局唯一。要设计全局 ID;手机号等全局唯一字段也不能只依赖每个分片上的唯一索引。

维护。 建索引、升级、备份需要覆盖所有分片。只恢复其中一个分片可能造成跨分片业务时间点不一致,要明确业务恢复与对账流程。

分区表、读写分离和分片的区别

方式 可以理解成 主要作用
MySQL 分区表 一张逻辑表内部按规则分区 按条件裁剪扫描、管理历史数据;通常仍属于同一实例
读写分离 主库写,副本分担读取 缓解读取压力;要处理复制延迟,写入仍集中在主库
分库分表 不同数据被路由到不同库表 分散数据和写入,需要新的路由与运维机制

例如按月管理审计日志,分区表可能已经足够;大量用户订单写入让一台机器撑不住,才进一步评估水平分片。MySQL 分区还有限制,例如分区表达式涉及的列必须包含在每一个唯一键内,设计前查对应版本的分区文档唯一键限制

扩容不是改一个取模数字

假设原来的四个分片已经很满,要扩到八个。我会按以下步骤准备迁移:

  1. 先确定新路由规则、数据归属和容量,保持迁移期间规则可追溯。
  2. 全量复制历史数据,同时记录增量起点。
  3. 用 binlog/CDC 等方式追平增量,检查更新与删除也已同步。
  4. 核对行数、校验值和关键业务结果,确认延迟达到切换要求。
  5. 按迁移工具能力暂停相关写入或使用受控迁移协议,切换读写路由。
  6. 观察错误、负载和数据一致性,再结束旧路由。

新分片开始接受写入以后,回滚不能只是指回旧库;必须先处理新写入数据,否则会丢业务记录。迁移方案要提前写清楚这个回退边界。

运维重点看这些

关注每个分片的容量、QPS、慢 SQL、复制延迟、连接数和热点差异,也关注跨分片请求占比、代理错误率和迁移进度。平均负载很低但一个分片满载,是典型的数据倾斜。

我理解分库分表的关键是:先让常用请求能准确找到一小部分数据,再评估它带来的事务、查询和扩容成本。拆得多本身并不是优化目标。