大表查询优化需分区裁剪与索引协同:分区键须为高频等值/范围查询字段,索引应建在分区键+过滤字段组合上,避免隐式转换、函数操作及低效分区。

大表查询慢,核心在于减少扫描数据量。分区裁剪负责“圈定范围”,索引负责“精准定位”,二者必须协同使用,单靠一个效果有限。
分区设计要匹配查询条件
分区字段必须是高频查询条件中的等值或范围列(如 create_time、region_id),否则无法触发裁剪。例如按月分区的表,若常查 WHERE status = 1 AND create_time BETWEEN '2024-01-01' AND '2024-01-31',就能只扫 202401 分区;但若只查 WHERE status = 1,所有分区仍会被遍历。
- 避免用非均匀分布字段(如用户性别)做分区键
- 尽量不跨分区查询,尤其避免 OR 条件破坏裁剪(如 dt='202401' OR dt='202402' 可能失效,改用 IN 更稳妥)
- 查看执行计划确认是否真正裁剪:EXPLAIN PARTITIONS SELECT ... 中 partitions 列应只显示少数几个分区名
索引要建在分区键+查询过滤字段的组合上
分区只是缩小扫描范围,每个分区内部仍需索引加速。局部索引(每个分区独立建索引)比全局索引更常用且高效。例如按 dt 分区的订单表,常查 user_id,就在每个分区内建 (user_id) 或复合索引 (user_id, status)。
- 不要在分区键上单独建索引(如 dt 字段本身已有分区逻辑,再索引冗余)
- 复合索引注意字段顺序:高选择性字段放前,如 (user_id, create_time) 比 (create_time, user_id) 对 WHERE user_id = ? 更有效
- 覆盖索引可进一步避免回表:把 SELECT 需要的字段也加入索引末尾,如 INDEX idx_uid_status_amt (user_id, status, amount)
避免隐式类型转换和函数操作破坏索引与裁剪
WHERE dt = 202401(dt 是字符串)会因类型不匹配导致分区裁剪失效;WHERE DATE(create_time) = '2024-01-01' 会让索引失效。所有条件都应保持原始字段类型和裸写形式。
- 字符串分区字段查询时加引号:WHERE dt = '202401'
- 时间范围用 >= / YEAR(create_time) = 2024
- LIKE 查询慎用前导通配符:LIKE '%abc' 无法走索引,尽量改为 LIKE 'abc%'
定期检查与清理低效分区
历史分区若长期无查询、数据量小但索引多,反而增加优化器决策负担。可通过慢查日志反查哪些分区从不被访问,归档或删除。
- 用 information_schema.PARTITIONS 查各分区行数与数据大小,识别明显倾斜
- 对只读历史分区,可考虑转为压缩表(如 ROW_FORMAT=COMPRESSED)降低 I/O
- 新增分区前预估数据增长节奏,避免分区过多(MySQL 单表建议不超过 1024 个分区)










