单表超千万行查询变慢通常因索引未用好、全表扫描或回表过多,而非数据量本身;应优先优化索引、避免隐式转换、慎用OFFSET分页,并在必要时按时间建子表而非原生分区。

单表超千万行后查询变慢,先别急着分表
绝大多数情况下,不是数据量本身压垮数据库,而是没用对索引、写了全表扫描的 WHERE 条件,或者 ORDER BY + LIMIT 在没覆盖索引时回表太多。MySQL 8.0 下,只要 created_at 上有 B+ 树索引,查最近 7 天数据通常毫秒级——前提是查询条件能命中索引最左前缀。
常见错误现象:EXPLAIN 显示 type=ALL 或 rows 接近总行数;SHOW PROFILE 发现大量 Copying to tmp table;慢日志里反复出现带 OFFSET 的分页查询。
- 先用
EXPLAIN FORMAT=JSON看执行计划,重点盯key(是否用了索引)、filtered(过滤率)、rows_examined(实际扫描行数) - 避免
SELECT *,只查需要字段,减少网络和内存开销 - 时间范围查询务必确保字段类型一致:比如
created_at是DATETIME,就别用字符串'2024-01-01'比较,否则可能隐式转换导致索引失效
按时间建子表(如 order_202401)比原生分区更可控
MySQL 原生 PARTITION BY RANGE (TO_DAYS(created_at)) 看似省事,但运维成本高:新增分区要 ALTER TABLE ... REORGANIZE PARTITION,出错容易锁表;备份恢复时分区元数据易丢失;很多监控工具不识别分区表真实大小。
使用场景:业务能接受写入时路由(比如订单创建时根据日期拼接表名),且读请求基本带明确时间范围(如“查 2024 年 Q1 订单”)。
- 建表命名统一用
order_YYYYMM格式,便于 SQL 拼接和 DBA 识别 - 写入层加一层简单路由逻辑,比如 Python 里用
table_name = f"order_{dt.strftime('%Y%m')}" - 查询时如果时间跨月,就用
UNION ALL合并多个子表,注意各子表都得有对应索引,且UNION ALL不去重,性能比UNION高得多 - 别忘了给每个子表单独分析统计信息:
ANALYZE TABLE order_202401,否则优化器可能误判行数
分区表不是银弹,WHERE 条件没包含分区键就白搭
MySQL 分区裁剪(partition pruning)只在 WHERE 子句明确包含分区键时生效。比如按 TO_DAYS(created_at) 分区,但查询写的是 WHERE DATE(created_at) = '2024-01-01',函数包裹会导致无法裁剪,所有分区都会被扫描。
参数差异:RANGE COLUMNS(created_at) 比 RANGE (TO_DAYS(created_at)) 更安全,前者支持直接比较日期字面量,后者要求传入天数整数。
- 分区键必须是索引的一部分,否则建表会报错:
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function - 删除旧数据别用
DELETE FROM ... WHERE created_at ,改用 <code>ALTER TABLE t DROP PARTITION p2022,毫秒级完成 - 注意时区问题:如果应用写入用的是 UTC 时间,但分区按本地时区计算
TO_DAYS(),会导致数据落入错误分区
分表后跨时间聚合查询变麻烦,得靠应用层或物化视图兜底
比如“统计近 90 天每日订单量”,原来一条 SQL 就搞定,分表后得查 3 张表再 UNION ALL + GROUP BY。如果频繁执行,延迟和连接数压力明显上升。
性能影响:每次跨表聚合都要建立多个连接、合并结果集,网络往返和客户端内存占用翻倍;分区表虽不用拼 SQL,但跨分区 GROUP BY 仍需归并排序,CPU 消耗高。
- 高频聚合需求,建议每天凌晨用定时任务把前一日数据汇总到一张宽表(如
daily_order_summary),查询直读这张表 - 如果用 MySQL 8.0+,可考虑用
CREATE VIEW封装多表UNION ALL逻辑,但注意视图不提升性能,只是语法糖 - 真正复杂的 OLAP 类查询,别硬扛在 MySQL 里,导出到 ClickHouse 或 Doris 更合适——它们对时间范围扫描做了深度优化
时间维度拆分看着简单,实际最常被忽略的是数据一致性校验和跨表事务。比如转账记录分到不同月份表,一笔跨月操作就得靠应用层保证两表写入原子性,这比加个索引难多了。











