COUNT进阶(续):超大表去重计数的极致优化

简介: 本篇详解COUNT(DISTINCT)性能瓶颈与四大优化方案:① HyperLogLog(误差1–2%,极速);② 索引+GROUP BY加速精确统计;③ Bitmap(低基数场景);④ 预计算/物化视图。按精度、实时性、成本选型,告别半小时卡死!

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

上周我们讲了COUNT(*)在大表上的近似计数与HyperLogLog。这周继续聊COUNT的进阶话题——​去重计数​。你一定遇到过这样的需求:“查一下昨天的独立访客数”“统计这周活跃设备量”,直接用COUNT(DISTINCT user_id),10亿行表跑了半小时还没出结果,怎么办?

去重计数的两种场景

场景 需求 可接受误差
运营报表、趋势图 DAU、MAU 1-2%
财务、库存、对账 精确金额、订单数 0%

不同场景对精度的要求完全不同。下面的优化手段,按误差从大到小排列。


方案一:近似去重(HyperLogLog)—— 要快,能接受1-2%误差

HyperLogLog是一种概率算法,用固定内存(约16KB)估算去重元素数量。原理:将每个元素哈希,统计哈希值二进制表示中前导零的最大长度,通过这个信息推断去重总数。

适用场景: UV、DAU、独立IP、搜索词去重统计等。

实现方式:

  1. Redis HyperLogLog​(最常用)
import redis
r = redis.Redis()
for user_id in logs:
    r.pfadd("daily_uv:2026-06-02", user_id)
uv = r.pfcount("daily_uv:2026-06-02")  # 误差1%以内
  1. PostgreSQL + hll扩展
CREATE EXTENSION hll;
SELECT hll_cardinality(hll_add_agg(hll_hash_integer(user_id))) FROM logs;

方案二:精确去重,但用索引优化 —— 要准,也要尽量快

如果必须精确,可以通过索引设计减少扫描量。

技巧1:覆盖索引

COUNT(DISTINCT user_id) 只需要user_id列,如果(user_id)上有索引,InnoDB可以直接扫描索引而不是全表,大大减少I/O。

-- 确保user_id有索引
CREATE INDEX idx_user_id ON logs(user_id);
SELECT COUNT(DISTINCT user_id) FROM logs;

技巧2:使用GROUP BY代替DISTINCT

在某些数据库中,GROUP BY + 外层COUNT有时比COUNT(DISTINCT)更快(取决于优化器):

SELECT COUNT(*) FROM (SELECT user_id FROM logs GROUP BY user_id) t;

实测对比(1000万行,user_id有索引):

写法 耗时
COUNT(DISTINCT user_id) 12秒
GROUP BY子查询 11秒(差异不大)

技巧3:分桶计数

如果数据分布均匀,可以按某个维度分桶,分别计数后求和。例如按日期分区,每天分别COUNT(DISTINCT)再累加(需要保证桶间无重复)。


方案三:bitmap聚合 —— 极速精确去重(限低基数场景)

如果去重的列基数很低(比如只有几个值:性别、状态、类型),可以使用bitmap。每个值对应一个bit位,多个值做OR/AND操作极快。

实现方式: 使用PostgreSQL的bitmap扩展,或MySQL的SET类型。

适用场景: 标签系统、权限判断、漏斗分析中的“是否完成某动作”。


方案四:预计算/物化视图 —— 以空间换时间

对于固定维度的去重统计(如每日DAU),可以提前计算并存储结果,查询时直接读取。

实现方式:

  • 每日定时任务计算前一天的COUNT(DISTINCT user_id)存入统计表
  • 使用物化视图(PostgreSQL支持,MySQL需借助第三方工具)
方案 实时性 存储成本 适用场景
实时COUNT(DISTINCT) 实时 小表或低频查询
HyperLogLog 实时 极低 可接受误差的高频查询
预计算表 非实时(T+1) 固定报表、趋势图
物化视图 准实时(可刷新) 综合报表

优化决策树
deepseek_mermaid_20260603_1d174b.png


真实案例:某APP日活统计

  • 数据量:每日约5000万独立设备ID
  • 要求:实时展示当天DAU(可接受1%误差)
  • 方案:使用Redis HyperLogLog,每条日志pfadd,实时pfcount
  • 结果:内存占用约12KB/天,响应时间<10ms,误差<1.5%

如果要求精确,则采用T+1预计算:凌晨计算前一天的精确COUNT(DISTINCT device_id)存入MySQL,白天查询直接读结果。


总结

去重计数的优化没有“银弹”,关键在于根据业务对精度、实时性、成本的要求做出合理选择。HyperLogLog是误差容忍场景的利器,bitmap适合低基数,预计算适合固定报表。掌握了这些方案,你就能在“快”和“准”之间找到最佳平衡点。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
29天前
|
SQL 人工智能 自然语言处理
Vibe Coding 是什么?当“感觉编程”遇上数据库
Vibe Coding是2026年编程圈最火的概念之一,指开发者通过自然语言描述“感觉”或“意图”,由AI自动生成代码、调试、优化。本文从Vibe Coding的起源讲起,分析它如何改变数据库开发方式:从手写SQL到自然语言查询、从人工调索引到AI推荐、从经验运维到智能诊断。探讨这项趋势对DBA职业的影响,并给出拥抱变化的实用建议。技术会变,但人的判断力、审美和业务理解才是长期竞争力。
|
16天前
|
SQL 关系型数据库 MySQL
事务隔离级别选错了,数据可能被“吞”掉——从脏读到幻读,一次讲透
事务隔离级别是数据库并发控制的核心机制,但很多开发者和DBA对脏读、不可重复读、幻读的区别一知半解,遇到问题只能“加锁试试”。本文从四个隔离级别出发,用真实SQL案例讲透三种并发问题的本质差异,对比MySQL与PostgreSQL在默认隔离级别上的不同选择,并结合业务场景给出选型建议,帮助读者写出更可靠的事务代码。
|
17天前
|
SQL 关系型数据库 MySQL
执行计划进阶:读懂统计信息与基数估算,理解优化器的“思考方式”
执行计划是SQL优化的核心工具,但很多人只关注type和Extra,忽略了执行计划背后的决策依据——统计信息与基数估算。本文从优化器的决策逻辑出发,解释统计信息如何影响基数估算、基数估算如何决定执行计划的选择。通过真实案例展示统计信息过旧如何导致优化器“选错路”,以及如何通过更新统计信息、使用扩展统计等方法来纠正。帮助读者从“看懂执行计划”进阶到“理解优化器为什么这么选”。
|
23天前
|
存储 SQL 缓存
InnoDB索引结构深潜:B+Tree与回表机制的底层逻辑
索引是SQL性能优化的核心,但很多人只停留在“建索引就能快”的层面,对索引的底层结构缺乏认知。本文从B+Tree的数据结构出发,深入讲解聚簇索引与二级索引的存储差异、回表机制的工作流程及代价分析、覆盖索引消除回表的原理。
|
18天前
|
人工智能 Cloud Native 关系型数据库
MySQL 8.4 LTS来了!从8.0到8.4,DBA必须知道的5个核心变化
MySQL 8.0社区版将于2026年结束生命周期,8.4 LTS作为首个长期支持版本,提供5年超长支持周期(至2031年)。本文从InnoDB并行查询、Redo Log动态容量、默认认证插件变更、参数默认值调整、云原生适配五个维度,梳理DBA升级前必须掌握的核心变化,并提供升级检查清单。
|
23天前
|
SQL 安全 关系型数据库
死锁分析进阶:从日志到根因,一次搞定死锁排查
死锁是DBA最头疼的问题之一,但很多人看到SHOW ENGINE INNODB STATUS输出后仍然无从下手。本文从死锁的四种常见模式出发,拆解死锁日志的关键字段含义,建立从“发现死锁”到“定位根因”到“预防复发”的完整分析链。结合真实案例讲解如何识别不同表顺序、相同表不同条件、间隙锁、外键约束四类死锁的日志特征,并给出系统化的预防方法。
|
24天前
|
SQL 运维 自然语言处理
国产向量数据库有哪些?两大技术流派深度对比与选型指南
向量数据库是2026年数据库领域增长最快的细分赛道之一。本文从RAG应用和企业知识库的实际需求出发,系统梳理国产向量数据库的两大技术流派——独立向量数据库与融合型向量数据库,深入对比两者的架构差异、适用边界和选型逻辑。
|
24天前
|
SQL 关系型数据库 MySQL
从索引设计到执行计划:一条慢查询的“体检”全流程
慢查询优化不是孤立地看执行计划,而是要从索引设计、执行计划解读、统计信息更新到SQL改写形成完整闭环。本文从一条真实的慢查询出发,串联索引设计原则、执行计划关键字段的诊断价值、统计信息对优化器的影响,以及验证优化的标准流程,帮助读者建立系统化的SQL性能优化方法论。
|
25天前
|
SQL 关系型数据库 MySQL
SQL优化进阶:读懂执行计划,告别慢查询焦虑
慢查询优化的第一步不是猜索引,而是读懂执行计划。本文从执行计划的生成原理出发,系统讲解type、key_len、rows、filtered、Extra五个核心字段的业务含义和诊断价值。通过典型案例揭示全表扫描、索引失效、文件排序、临时表等常见性能陷阱的判定方法,并给出标准化的优化排查流程。帮助开发者从“凭感觉优化”升级到“基于证据优化”。
|
1月前
|
存储 SQL 关系型数据库
索引优化深潜(下):索引合并、ICP 与索引设计的实战法则
索引优化不止于单索引设计。本文深入讲解MySQL 5.6/8.0的高级索引特性:索引合并(Index Merge)的三种策略(交集、并集、排序并集)、索引条件下推(ICP)的工作原理和适用场景,以及如何使用MRR(多范围读取)优化随机I/O。通过真实案例演示如何利用这些特性提升查询性能,同时揭示索引合并可能带来的隐患。最后总结索引设计实战法则,帮助你从“能用索引”进阶到“用好索引”。