SQL 索引选择性对查询性能影响

2次阅读

索引选择性低、复合索引顺序不当、NULL值处理不当及统计信息过期均会导致优化器弃用索引;应计算选择性(

SQL 索引选择性对查询性能影响

索引选择性低会导致全表扫描

WHERE 条件列的重复值太多(比如 status 只有 ‘active’/’inactive’ 两种),mysqlpostgresql 可能直接放弃走索引,改用全表扫描——因为读索引再回表的成本比直接扫一遍还高。

实操建议:

  • select count(DISTINCT col) / COUNT(*) FROM table 算选择性,结果低于 0.01(即 1%)就危险
  • 避免给 genderis_deleted 这类低基数字段单独建索引
  • 如果必须查这类字段,优先考虑组合索引,把低选择性列放在后面,比如 (created_at, status) 而不是 (status, created_at)

复合索引顺序决定能否命中

数据库只能按索引定义的**最左前缀**匹配条件。写成 INDEX (a, b, c),那么 WHERE a = ? AND b = ? 能用上,但 WHERE b = ? AND c = ? 就完全失效。

实操建议:

  • 高频等值查询字段放最左,范围查询(BETWEEN>)字段放中间,排序/分组字段放最后
  • 如果有 WHERE user_id = ? AND created_at > ? ORDER BY updated_at DESC,索引应为 (user_id, created_at, updated_at)
  • 不要为了“看起来全面”砌字段,每多一个字段都增加索引体积和写入开销

NULL 值让索引失效的隐蔽情况

某些场景下,IS NULLIS NOT NULL 不走索引,尤其在 MySQL 5.7 之前;PostgreSQL 对 IS NULL 支持较好,但若字段允许 NULL 且实际 NULL 值很多,选择性也会被拉低。

实操建议:

  • 建表时尽量用 NOT NULL + 默认值(如 0''),除非业务真需要区分“未设置”和“空”
  • IS NULL 前先确认执行计划:EXPLAIN SELECT ...,看 key 列是否为空
  • 如果必须支持 NULL 查询且性能差,可加生成列(MySQL 5.7+ 的 STORED 列)或函数索引(PostgreSQL 的 CREATE INDEX ON t ((col IS NULL))

统计信息过期会让优化器选错索引

PostgreSQL 的 ANALYZE、MySQL 的索引统计(innodb_stats_persistent 相关)如果不更新,优化器可能基于错误的选择性估算,跳过本该用的索引,甚至选错连接顺序。

实操建议:

  • 大表批量导入后立刻执行 ANALYZE table_name(PostgreSQL)或 ANALYZE TABLE table_name(MySQL)
  • MySQL 中检查 information_schema.STATISTICS 表的 SEQ_IN_INDEXCARDINALITY,确认数值是否合理
  • 不要依赖自动统计——某些 OLAP 场景下,自动采样率太低,innodb_stats_sample_pages 可调高,但别盲目设成 2000+

真正卡住性能的,往往不是没建索引,而是索引建了但优化器不敢用、或者用了却绕远路。验证方式永远只有两个:看 EXPLAIN,再看真实 EXPLAIN ANALYZE(PostgreSQL)或 EXPLAIN FORMAT=json(MySQL)里的实际行数和时间分布。

text=ZqhQzanResources