要准确评估 SELECT COUNT(DISTINCT column_name) 真实耗时,需先检查该列是否有有效索引、确认基数(cardinality/n_distinct)是否准确,并结合 EXPLAIN ANALYZE 观察实际执行路径;无索引或基数统计失真时易触发全表扫描,此时应考虑采样估算或优化索引策略。
怎么看 SELECT COUNT(DISTINCT column_name) 真实耗时?
直接跑 count(distinct) 在大表上很可能卡住,不是语法错,是执行计划没走索引或触发全表扫描。mysql 8.0+、postgresql 默认对 count(distinct) 不用索引;sqlite 根本不优化这个聚合。
- 先确认该列有没有单列索引:
SHOW INDEX FROM table_name WHERE Column_name = 'column_name';(MySQL)或\d table_name(PostgreSQL) - 如果没索引,
COUNT(DISTINCT)就是硬扫,别指望快;加索引前先看重复率——基数太低(比如性别字段只有 2 值),索引收益几乎为零 - 替代方案:用采样估算。PostgreSQL 可跑
SELECT APPROX_COUNT_DISTINCT(column_name) FROM table_name;;MySQL 没原生函数,得靠SELECT COUNT(*) FROM (SELECT DISTINCT column_name FROM table_name LIMIT 10000) t;手动截断,但结果只是下界
为什么 EXPLAIN 显示用了索引,但 cardinality 值低得离谱?
cardinality 是 MySQL 的统计值,不是实时更新的。它只在 ANALYZE TABLE 或自动采样时刷新,且采样行数有限(默认约 1/16 表数据),遇到倾斜分布(比如 95% 值是 'unknown')就会严重低估真实基数。
- 执行
ANALYZE TABLE table_name;强制更新统计信息,再查SHOW INDEX FROM table_name; - 若仍不准,可调高采样精度:
SET SESSION innodb_stats_sample_pages = 64;(需有权限,且只对当前会话生效) - 注意:InnoDB 的
cardinality对复合索引只反映最左前缀列的分布,比如索引(a, b),cardinality只代表a的去重数,和b无关
PostgreSQL 里 pg_stats 的 n_distinct 为负数是什么意思?
负值表示 PostgreSQL 估算出的基数超过表总行数,即认为该列“基本唯一”。例如 n_distinct = -1 表示它推断该列无重复值(如主键或 UUID 字段);n_distinct = -0.5 表示估算唯一值占总行数的 50%。
- 查法:
SELECT tablename, attname, n_distinct FROM pg_stats WHERE tablename = 'table_name' AND attname = 'column_name'; - 这个值影响查询计划:如果
n_distinct被严重高估(比如把高重复字段标成 -1),优化器可能错误选择 Nested Loop 而非 Hash Join,拖慢 JOIN 性能 - 手动更新统计:
ANALYZE table_name (column_name);,比全表ANALYZE更轻量,适合高频更新的列
索引效率真和基数强相关?哪些情况基数高但索引照样慢?
基数只是前提,不是充分条件。高基数列如果查询条件匹配度极低(比如查一个只出现 3 次的值),B-Tree 索引仍要跳转多次;更糟的是,如果该列常和低基数列组合 WHERE,而你建的是单列索引,优化器大概率弃用它。
- 检查实际索引使用:
EXPLAIN ANALYZE SELECT * FROM table_name WHERE column_name = 'rare_value';,重点看rows和actual rows是否接近 - 避免“假高基数”陷阱:时间戳列基数理论上极高,但业务查询常是范围(
BETWEEN '2024-01-01' AND '2024-01-02'),这时 B-Tree 效率取决于范围宽度,不是单点等值 - 复合索引顺序必须匹配查询模式。例如
WHERE status = 'active' AND created_at > '2024-01-01',索引应为(status, created_at),而非反过来——哪怕created_at基数更高
基数统计本身是估算,索引是否生效还得看具体查询条件、数据物理分布、缓冲池命中率。别只盯着 cardinality 或 n_distinct 数字做决定,一定结合 EXPLAIN ANALYZE 的实际执行路径看。

















