统计查询慢应先检查索引覆盖:对等值字段(如status)和范围字段(如created_at)建联合索引,避免全表扫描、函数导致索引失效;高频统计优先用mysql 8.0+窗口函数替代自连接。

统计查询慢?先确认是否用了索引覆盖
MySQL 统计类查询(比如 select count(*) FROM orders WHERE status = 'paid' AND created_at >= '2024-01-01')卡顿,大概率不是 SQL 写得不对,而是缺少针对性索引。别急着加 COUNT(*) 或改写子查询——先看执行计划:EXPLAIN 输出里如果 type 是 ALL 或 index,且 Extra 出现 using where,说明在全表/全索引扫描后才过滤。
实操建议:
- 对高频统计条件字段组合建联合索引,顺序按「等值查询字段 + 范围查询字段」排列,例如
INDEX idx_status_created (status, created_at) - 若只查行数且无
WHERE,COUNT(*)在 InnoDB 下会走聚簇索引的最小叶子页计数(快),但加了条件后必须依赖二级索引覆盖——确保SELECT的字段都在索引中,避免回表 - 避免在统计字段上用函数,如
WHERE date(created_at) = '2024-01-01'会让索引失效;改用created_at >= '2024-01-01' AND created_at
实时统计不准?考虑汇总表 + 定时刷新
当订单、日志这类大表的统计需求要求「准实时」(比如后台看板每分钟更新),硬扛 SUM()/COUNT() 全表聚合必然拖垮数据库。MySQL 本身不提供物化视图,但可以用「汇总表 + 事件调度器」模拟。
实操建议:
- 新建汇总表,例如
daily_order_summary,字段含stat_date DATE、status VARCHAR(20)、order_count int、total_amount DECIMAL - 用
INSERT ... SELECT每小时/每天跑一次聚合,注意加ON DUPLICATE KEY UPDATE处理重复调度 - 启用
event_scheduler = ON,创建事件:CREATE EVENT ev_daily_summary ON SCHEDULE EVERY 1 HOUR DO INSERT INTO daily_order_summary (...) SELECT CURDATE(), status, COUNT(*), SUM(amount) FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR) GROUP BY status ON DUPLICATE KEY UPDATE ...;
需要分组+时间窗口统计?优先用窗口函数而非自连接
比如「每个用户最近 3 笔订单金额」或「每日新增用户中,7 日内复购率」,传统写法容易陷入多层子查询或自连接,性能差还难维护。MySQL 8.0+ 原生支持窗口函数,应直接使用。
实操建议:
- 用
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)取 Top-N,比GROUP_CONCAT + SUBSTRING_INDEX更可靠 - 时间窗口类计算(如滚动 7 日 UV)慎用
LAG()/LEAD(),它们只能取固定偏移行;更稳妥的是用DATE_SUB配合关联子查询,或提前生成日期维表做 JOIN - 窗口函数不能直接用于
WHERE过滤(如WHERE rn ),需套一层派生表,否则报错 <code>Window function is not allowed in WHERE clause
统计结果要导出或对接 BI?别在应用层拼 SQL
后台系统常需把统计结果导出 CSV、推送到 Grafana 或供前端 ECharts 渲染。如果每次请求都现场执行聚合 SQL,一来并发高时 DB 压力大,二来 SQL 参数拼接易引发注入或类型错误。
实操建议:
- 封装成存储过程,参数用
IN显式声明,例如CREATE PROCEDURE sp_get_sales_by_region(IN p_month CHAR(7)),调用时传入'2024-01' - 导出用
SELECT ... INTO OUTFILE(注意 MySQL 用户需有FILE权限,且路径是服务端路径);BI 对接优先走只读从库,避免主库被长查询拖慢 - 对高频低变统计(如各渠道占比),可定时写入 JSON 字段缓存到配置表,应用层直接读,省去解析逻辑
真正卡住统计系统的往往不是语法,而是没想清楚「这个指标到底要多准、多快、谁在用」。索引设计漏掉范围条件、汇总表忘记处理时区、窗口函数套错层级——这些细节比学多少函数都关键。