MySQL单表千万级数据,分表方案
当MySQL单表数据量超过千万级,最直接的感受是查询变慢、写入锁竞争加剧。
解决方向有两个:分表降低单表数据量,索引优化减少扫描行数。
本文围绕这两个方向,给出可落地的判据、方案和命令,适合开发、运维和自学数据库优化的读者。
先判断:你的表真的需要分表吗?
分表不是数据量大就一定要做。
先看两个指标:单表行数是否长期超过一千万,或表容量超过10GB;
慢查询日志中是否频繁出现全表扫描。
用下面SQL确认:
SELECT table_rows, ROUND(data_length/1024/1024, 2) AS data_mb
FROM information_schema.tables
WHERE table_schema = '你的库名' AND table_name = '你的表名';
如果只有查询慢,但写入量不大,优先优化索引;
如果写入和查询都明显下降,才考虑分表。
分表方案:水平分表与垂直分表怎么选
水平分表是把同一张表的数据按规则拆到多张结构相同的表里,比如按用户ID取模拆分,适用于用户表、订单表。
垂直分表是把字段拆分到不同表,把大字段、不常用字段拆出去,适用于单行数据很大的场景。
实操时注意:分表键要和业务查询条件匹配。
比如订单表按 user_id 取模,查询订单时就必须带上 user_id,否则需要遍历所有分表。
业务不兼容时,建议引入分库分表中间件,如 ShardingSphere,避免在应用层写死路由规则。
索引优化实操:先定位慢查询,再建对索引
分表前先把慢查询解决,分表后索引同样不能省。
开启慢查询日志:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
用 EXPLAIN 查看执行计划,重点看 type 和 rows 字段。
如果 type=ALL 说明全表扫描,需要加索引。
常见优化手段:
- 联合索引要遵循最左前缀原则,例如
(user_id, status)能支持user_id单独查询和user_id+status组合查询。 - 覆盖索引避免回表,查询字段尽量包含在索引列中。
- 索引不是越多越好,写多读少的表每新增一个索引都会拖慢写入。
创建索引示例:
ALTER TABLE 订单表 ADD INDEX idx_user_status (user_id, status);
创建后再次用 EXPLAIN 确认 key 字段已走到新索引。
分表后的常见坑与避坑说明
分表不是一劳永逸,下面几个坑很常见:
- 主键冲突:分表后不能依赖自增ID,建议使用雪花算法生成全局唯一ID,或使用 MySQL 的
UUID_SHORT()。 - 跨分页查询:
ORDER BY和分页需要先汇总多个分表数据再排序,数据量越大越慢。 - 表结构调整困难:分表后 DDL 要在所有分表上执行,建议用 gh-ost 或 pt-online-schema-change 这类工具平滑处理。
- 数据倾斜:取模分表时如果某个ID范围数据量特别大,会导致单表依然很大。要结合业务分布选择分表键。
效果验证:用数据说话
操作完成后,按步骤验证:
- 用
SHOW TABLE STATUS或information_schema确认各分表行数相对均匀。 - 跑一遍之前的慢查询,对比耗时。
- 用
SHOW INDEX FROM 表名确认索引已建立。
例如优化前慢查询耗时 3.2 秒,分表加索引后耗时 80 毫秒,说明方案有效。
如果效果不明显,检查是否出现全表扫描,或者分表键与查询条件不匹配。
如果你正在处理 MySQL 单表千万级数据的分表方案与索引优化实操,建议先按本文步骤完整执行,再根据业务情况调整。
分表和索引是持久优化过程,保持监控和定期复查执行计划,才能让数据库持续保持在健康状态。