索引选择性差本身不是错误,而是优化器放弃索引的合理依据;真正需解决的是误判为差(实际可用)或本就差却强行依赖导致的性能崩塌。

索引选择性差本身不是“错误”,而是优化器放弃该索引的合理依据;真正要解决的,是当它被误判为“差”(实际可用),或本就差却强行依赖时引发的性能崩塌。
为什么 EXPLAIN 显示 key = NULL 却查的是高基数字段?
常见于字段值分布突变但统计信息未更新:比如原本 status 有 5 种取值,某次批量导入后 95% 的新记录变成 status = 1,而 ANALYZE TABLE 没跑过。优化器仍按旧统计估算“status = 1 只占 20%”,实际扫 95%,于是直接弃用索引。
- 先查真实分布:
SELECT status, COUNT(*) FROM t GROUP BY status ORDER BY COUNT(*) DESC LIMIT 5; - 再对比统计值:
SELECT COLUMN_NAME, CARDINALITY FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't' AND INDEX_NAME = 'idx_status'; - 若两者偏差 >30%,立刻执行
ANALYZE TABLE t;,别等自动触发
明明 CARDINALITY 很高,为什么优化器还是不用索引?
因为选择性 ≠ 基数。优化器真正看的是“该条件能过滤多少行”,而 CARDINALITY 只反映值种类数量。例如手机号字段 CARDINALITY 接近总行数,但查询 WHERE mobile LIKE '1%' 实际命中 80% 行——优化器判断走索引再回表成本 > 全表扫描,于是跳过。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 用
EXPLAIN FORMAT=JSON查filtered字段:它才是优化器对当前条件过滤率的预估(如"filtered": 1.2表示预计只留下 1.2% 行) - 若
filtered明显偏高(>25%),说明即使索引存在,优化器也认为不值得用 - 此时加索引无意义,应改写查询(如拆分范围)、加覆盖索引,或建直方图(MySQL 8.0+)
什么时候该用直方图,而不是硬加索引?
直方图是给优化器看的“数据分布快照”,专治那些基数高但局部倾斜的字段(如 created_at 近期写入密集、历史数据稀疏)。它不改变索引结构,但能让优化器知道“WHERE created_at > '2026-10-01' 其实只扫 0.3% 行”。
- 创建命令:
ANALYZE TABLE t UPDATE HISTOGRAM ON created_at;(默认等宽直方图) - 查看效果:
SELECT * FROM INFORMATION_SCHEMA.COLUMN_STATISTICS WHERE TABLE_NAME = 't'; - 注意:直方图不会自动更新,需定期重采;且仅影响
WHERE条件估算,不影响JOIN或ORDER BY - 别和索引混淆:直方图不加速查询,只帮优化器做对决定;索引才真正加速定位
最易被忽略的一点:选择性差的字段上建索引,往往不是为了单条件查询,而是作为联合索引的前缀。比如 (status, created_at) 中 status 本身选择性差,但配合时间范围就能高效剪枝——这时问题不在 status,而在联合索引是否覆盖了查询的所有条件。

















