优化拥有2000万条记录的Django站点MySQL总记录数统计速度
Django大数据量下COUNT(DISTINCT)性能优化方案
改用覆盖索引
你当前的查询是基于多个过滤条件统计唯一OGRN/OGRNIP的数量,普通单字段索引可能无法满足需求。建议创建包含所有过滤字段+目标唯一字段的联合覆盖索引,让数据库无需回表查询,直接从索引中完成统计计算。
示例(以公司表为例,按过滤条件的筛选力度从高到低排序字段):CREATE INDEX idx_company_filter_ogrn ON company_table (email_field, okved_field, capital_field, OGRN);注意:联合索引的字段顺序很关键,把过滤时能筛掉更多数据的字段放在前面。
允许近似统计的话,换用近似方案
如果业务场景不需要绝对精确的数值,可以用数据库自带的近似统计功能:- MySQL:通过
EXPLAIN语句的rows字段估算结果数; - PostgreSQL:查询
pg_stat_user_tables表的n_live_tup字段获取近似行数;
也可以用Redis缓存近似值,定期更新,前端显示时标注“约XX条”,能把查询耗时降到毫秒级。
- MySQL:通过
预计算统计值(离线聚合)
对实时性要求不高的场景,用定时任务(比如Django的Celery+Beat)定期跑统计任务,把结果存在单独的统计表或Redis中:- 每天凌晨执行全量统计,覆盖所有常用过滤条件组合的结果;
- 或者做增量更新:每次新增/删除数据时,同步更新对应统计值的计数;
查询时直接读取预计算好的值,完全避免对大表的实时扫描。
优化查询语句逻辑
- 检查WHERE条件里的字段是否存在隐式类型转换(比如字符串字段用数字匹配),这会直接导致索引失效;
- 清理
from_clause里不必要的关联表,减少数据扫描范围; - 把
COUNT(DISTINCT)改成子查询先去重再计数,部分数据库优化器对这种写法的处理效率更高:SELECT COUNT(*) FROM (SELECT DISTINCT cs.OGRN FROM ... WHERE ...) AS tmp;
数据库层面的进阶优化
- 分区表:将大表按时间、地区或核心过滤字段分区,查询时只扫描符合条件的分区,大幅减少扫描的数据量;
- 调整数据库配置:加大内存让更多数据缓存到内存中,优化查询缓存(如果适用);如果业务允许,MyISAM引擎的
COUNT(*)性能比InnoDB好,但要注意它不支持事务和行级锁。
内容的提问来源于stack exchange,提问作者Mlofor
相关产品推荐
相关产品推荐

