enum字段加索引却全表扫描,主因是mysql优化器对基数估算失真,尤其数据倾斜时误判区分度低而跳过索引;验证需看explain的key列与show index的cardinality,修复可analyze table或改用tinyint+字典表。

ENUM字段加了索引却走全表扫描?先查Cardinality
根本不是索引没建,而是MySQL优化器对ENUM的基数估算严重失真——尤其当实际数据分布倾斜(比如95%是'active')时,它会误判“这列区分度太低”,直接跳过索引。
验证方式很简单:EXPLAIN SELECT * FROM orders WHERE status = 'shipped';,如果key列为空、rows接近总行数,就是被跳过了。
- 先看统计是否可信:
SHOW INDEX FROM orders;,检查Cardinality值是否远低于你定义的枚举值个数(比如定义了5个值但显示为1或2) - 强制刷新统计:
ANALYZE TABLE orders;,有时能立刻恢复索引使用 - 若仍无效,说明优化器已彻底放弃该列索引,得换方案
WHERE条件必须写字符串,不能用数字比较
ENUM查询语义绑定的是字符串字面量,不是底层整数。写WHERE status = 2不仅不走索引,还会导致结果错乱:定义顺序是('draft','pending','done')时,2对应'pending';但后续增删枚举值后,数字映射就变了。
- ✅ 正确写法:
WHERE status = 'done'、WHERE status IN ('draft', 'pending') - ❌ 错误写法:
WHERE status = 2、WHERE status + 0 > 1(绕过索引且破坏可维护性)
IN能走索引,BETWEEN和>基本无效
MySQL不支持基于枚举定义顺序的范围扫描,BETWEEN、>、会被转成字符串比较,无法利用B+树索引。
- 有效:
WHERE status IN ('draft', 'pending')→ 索引命中(等价于多个等值查找) - 无效:
WHERE status > 'draft'→ 全表扫描(即使逻辑上只有两个后续值) -
ORDER BY status能走索引避免filesort,但排序结果严格按定义顺序,不是字母序,也不随业务含义变化
TINYINT + 字典表才是长期可控的选择
当状态需要频繁查询、排序、扩展,或DBA需要稳定执行计划时,ENUM就成了黑盒。换成TINYINT UNSIGNED加应用层映射,或外键关联字典表,收益明确。
- 建表示例:
status TINYINT UNSIGNED NOT NULL CHECK (status IN (1,2,3)),再建普通索引 - 优势:范围查询、基数估算、执行计划全部可控;ALTER添加新状态不锁表;排序可配合
sort_order字段灵活定制 - 注释要跟上:
-- 1=active, 2=inactive, 3=archived,比ENUM('active','inactive','archived')更易维护
真正难的不是选ENUM还是TINYINT,而是意识到:一旦表数据量上千万、QPS持续升高,ENUM的隐式行为就会变成性能黑洞——而这个拐点,往往在监控里看不到,只在慢查日志里突然冒出来。











