聚合查询前须建索引:对where条件与聚合字段建联合索引,避免全表扫描;禁用聚合函数内表达式;高频统计用缓存;大数据量用show table status估算;分组聚合需为group by字段建索引。

聚合查询前先加索引
当对大表执行 COUNT、SUM、AVG 等聚合操作时,若字段未建索引,MySQL 会强制全表扫描,10万行数据可能耗时300ms以上。
确认聚合字段是否已索引:SHOW INDEX FROM user WHERE Column_name = 'status';
对 WHERE 条件 + 聚合字段组合建联合索引,例如统计「状态为1的用户平均积分」:ALTER TABLE user ADD INDEX idx_status_score (status, score);
【必须确保 status 字段为 NOT NULL,否则索引可能失效】
用 EXPLAIN 验证:EXPLAIN SELECT AVG(score) FROM user WHERE status = 1; 若 type 显示为 ref 或 const,且 key 显示 idx_status_score,说明索引生效。
避免在聚合函数中使用表达式
错误写法:Db::name('order')->avg('amount * 1.1'); 这会让 MySQL 无法利用 amount 字段上的索引,强制计算每行后再求均值。
正确做法是先算好基础值再聚合:Db::name('order')->avg('amount'); 后端 PHP 层统一乘 1.1。
同理,禁止在 COUNT 中传入函数或 CASE WHEN:COUNT(IF(status=1, id, NULL)) 会跳过索引,改用 WHERE 精确过滤后 COUNT(*)。
用缓存绕过重复聚合
高频但低频更新的统计结果(如“今日订单总数”“平台总用户数”)应直接缓存。
方法一:使用 cache() 包裹聚合调用,设置 60 秒过期:
cache('total_users', function () { return Db::name('user')->count(); }, 60);
方法二:在模型中封装带缓存逻辑的统计方法,首次调用查库并写缓存,后续直接读缓存。
注意:若业务要求强一致性(如财务类实时汇总),则不能缓存,需走数据库直查。
大数据量下改用近似统计
当表行数超500万且只需数量级参考时,放弃精确 COUNT(*),改用 MySQL 的估算值:
第一步:执行 SHOW TABLE STATUS LIKE 'article';
第二步:从 Rows 字段读取预估行数(误差通常在 ±10% 内);
第三步:在 ThinkPHP 中用 Db::query() 直接获取该值:
$res = Db::query("SHOW TABLE STATUS LIKE 'article'");
$approxCount = $res[0]['Rows'] ?? 0;
这一步比 COUNT(*) 快 100 倍以上,适用于后台监控大盘、管理端概览页等场景。
分组聚合务必加 GROUP BY 索引
执行 GROUP BY city → COUNT(*) 统计各城市用户数时,若 city 字段无索引,MySQL 会先排序再分组,内存临时表+文件排序开销极大。
① 给分组字段单独建索引:ALTER TABLE user ADD INDEX idx_city (city);
② 若同时带 WHERE 条件(如 WHERE status = 1),优先建联合索引:ALTER TABLE user ADD INDEX idx_status_city (status, city);
③ 验证是否触发索引优化:EXPLAIN SELECT city, COUNT(*) FROM user WHERE status = 1 GROUP BY city; 检查 Extra 是否含 "Using index"。
这一步不做,10万行分组聚合可能从 80ms 拉长到 1.2 秒。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











