bigywater.tech
MySQL / InnoDB · 76

隐式类型转换导致索引失效

给同一个VARCHAR索引列分别用字符串和数字字面量做查询条件,看隐式类型转换真实是怎么把一次索引查找降级成全索引扫描的。

cost 0.35→101
字面量类型不匹配后代价真实暴涨的倍数
扫描全部1003行
隐式转换后索引真实退化成了全量扫描

做的事:同一个索引列,条件分别用字符串和数字字面量

代码CREATE TABLE str_idx (id INT PRIMARY KEY, code VARCHAR(20)); CREATE INDEX idx_code ON str_idx(code); SELECT * FROM str_idx WHERE code = '1001'; -- 字符串字面量,类型匹配 SELECT * FROM str_idx WHERE code = 1001; -- 数字字面量,触发隐式转换

验证:类型匹配时,真实是一次精确的索引定位

实测 · 本机EXPLAIN FORMAT=TREE输出
WHERE code = '1001':
-> Covering index lookup on str_idx using idx_code (code = '1001')  (cost=0.35 rows=1)

字符串字面量和VARCHAR列的类型真实完全匹配,MySQL能直接拿'1001'去idx_code这棵索引树上做一次精确查找,真实估算只要扫1行、代价0.35。

验证:数字字面量,索引真实降级成了"扫描+过滤"

实测 · 本机EXPLAIN FORMAT=TREE输出
WHERE code = 1001:
-> Filter: (str_idx.`code` = 1001)  (cost=101 rows=100)
    -> Covering index scan on str_idx using idx_code  (cost=101 rows=1003)   ← 真实扫了全部1003行
当条件写成code = 1001(数字字面量)时,MySQL的比较规则真实要求把两边转换成同一种类型进行比较——按照真实的转换规则,字符串列会被转换成数字(而不是把数字转换成字符串),这意味着索引里按字符串顺序排好的'1001',没法直接拿数字1001去做树查找了,因为索引树的排序是按字符串比较建立的,不是按转换后的数值。真实的执行计划变成了"把整个索引从头扫到尾(Covering index scan,扫了全部1003行),每一行都转换类型之后再比较(Filter)"——虽然还是走了索引(比全表扫描省了不需要访问主键聚簇数据的开销),但真实已经不是索引定位查找了,退化成了扫描全部索引条目。代价数字从0.35暴涨到101,真实印证了这一点。

分步演示:同一个索引,两种字面量类型的真实差异

每一步都是本文上面用EXPLAIN FORMAT=TREE跑出来的原始结果。

字面量类型对不对,决定索引是查找还是扫描
这一步新验证的写法

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),VARCHAR列上建索引,对比字符串和数字字面量的查询
    • EXPLAIN FORMAT=TREE:区分Index lookup和Index scan+Filter
    京ICP备2026067502号-1京公网安备11010502063487号