统计查询慢?先确认是否用了索引覆盖
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 字段缓存到配置表,应用层直接读,省去解析逻辑 真正卡住统计系统的往往不是语法,而是没想清楚「这个指标到底要多准、多快、谁在用」。索引设计漏掉范围条件、汇总表忘记处理时区、窗口函数套错层级——这些细节比学多少函数都关键。
