Skip to content

索引失效的场景

查询条件做函数计算

sql
select count(*) from tradelog where month(t_modified)=7;

查询条件做函数计算,在查索引的时候,利用不了索引。因为索引利用的是树的有序性,但是函数计算后的结果在索引的B+树上并不连续。MySQL在查询的时候利用不到树的有序性。

但并不是完全不利用索引,MySQL在选择索引的时候,判断该字段的索引要比主键索引更小,所以还是会查询该字段 t_modified 对应索引,但是会查询整棵树。

对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。

隐式类型转换

假如 tradeid 字段类型是 varchar ,查询语句

sql
explain   select * from tradelog where tradeid=3;

查询结果走了全表查询。

原因是因为 varchar 类型 和 int 类型做对比的时候,会将 varchar 做类型转换为 int,导致索引失效。类型转换的过程就是在 tradeid 字段做了函数计算。


反过来 id 为 int 类型,查询条件是 varchar 时,会走索引。因为将 varchar 转为 int,并没有在索引字段做函数计算。

sql
explain   select * from tradelog where id="3";

字符集不同导致索引失效

在MySQL中 utf8mb4 是 utf8 的超集。

如果两个表A、B分别为 utf8mb4、utf8 时,匹配时 MySQL内部会将 utf8 转换为 utf8mb4。转换过程也是一个函数计算,可能导致索引失效。

  • 第一种场景

    sql
    select a.*,b.* from A a,B b where a.id = 1 and a.name = b.name

    此时根据 查到 a 的 name 之后,查 b 表变为

    sql
    select * from b where b.name = "123"(utf8mb4)
    等价于
    select * from b where CONVERT(name USING utf8mb4) = "123"(utf8mb4)

    在查询条件字段上做函数计算,导致索引不生效。

  • 第二种场景

    sql
    select a.*,b.* from A a,B b where b.id = 1 and a.name = b.name

    此时根据 查到 b 的 name 之后,查 a表变为

    sql
    select * from a where a.name = "123"(utf8)
    等价于
    select * from b where a(utf8mb4) = CONVERT("123" USING utf8mb4)

    在查询条件字段上没有做函数计算,索引继续生效。

优化方法

  1. 保证两张表的字符集问题,优先使用 utf8mb4。

  2. 显式转换查询条件的字符集。

    sql
    select * from b where CONVERT(name USING utf8mb4) = "123"(utf8mb4)
    转为
    select * from b where name(utf8) = CONVERT("123" USING utf8)