zoukankan      html  css  js  c++  java
  • Mysql索引失效原因

    1.有or必不使用索引;
    2.复合索引未用左列字段;
    3.like以%开头;
    4.需要类型转换;
    5.where中索引列有运算;
    6.where中索引列使用了函数;
    7.如果mysql觉得全表扫描更快时(数据少);

    1. where语句中包含or时,可能会导致索引失效

    使用or并不是一定会使索引失效,你需要看or左右两边的查询列是否命中相同的索引。

    假设USER表中的user_id列有索引,age列没有索引。

    下面这条语句其实是命中索引的(据说是新版本的MySQL才可以,如果你使用的是老版本的MySQL,可以使用explain验证下)。

    1
    select * from `user` where user_id = 1 or user_id = 2;

    但是这条语句是无法命中索引的。

    1
    select * from `user` where user_id = 1 or age = 20;

    假设age列也有索引的话,依然是无法命中索引的。

    1
    select * from `user` where user_id = 1 or age = 20;

    因此才有建议说,尽量避免使用or语句,可以根据情况尽量使用union all或者in来代替,这两个语句的执行效率也比or好些。

    2. where语句中索引列使用了负向查询,可能会导致索引失效

    负向查询包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等。

    某“军规”中说,使用负向查询一定会索引失效,笔者查了些文章,有网友对这点进行了反驳并举证。

    其实负向查询并不绝对会索引失效,这要看MySQL优化器的判断,全表扫描或者走索引哪个成本低了。

    3. 索引字段可以为null,使用is null或is not null时,可能会导致索引失效

    其实单个索引字段,使用is null或is not null时,是可以命中索引的,但网友在举证时说两个不同索引字段用or连接时,索引就失效了,笔者认为确实索引失效,但这个锅应该由or来背,属于第一种场景~~

    假设USER表中的user_id列有索引且允许null,age列有索引且允许null。

    1
    select * from `user` where user_id is not null or age is not null;

    不过某些“军规”和规范中都有强调,字段要设为not null并提供默认值,是有原因值得参考的。

    • null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化。
    • null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多。
    • null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识。
    • 对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!='shenjian',如果存在name为null值的记录,查询结果就不会包含name为null值的记录。

    4. 在索引列上使用内置函数,一定会导致索引失效

    比如下面语句中索引列login_time上使用了函数,会索引失效:

    1
    select * from `user` where DATE_ADD(login_time, INTERVAL 1 DAY) = 7;

    优化建议,尽量在应用程序中进行计算和转换。

    其实还有网友提到的两种索引失效场景,应该都归于索引列使用了函数。

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

    比如下面语句中索引列user_id为varchar类型,不会命中索引:

    1
    select * from `user` where user_id = 12;

    这是因为MySQL做了隐式类型转换,调用函数将user_id做了转换。

    1
    select * from `user` where CAST(user_id AS signed int) = 12;

    4.2 隐式字符编码转换导致的索引失效

    当两个表之间做关联查询时,如果两个表中关联的字段字符编码不一致的话,MySQL可能会调用CONVERT函数,将不同的字符编码进行隐式转换从而达到统一。作用到关联的字段时,就会导致索引失效。

    比如下面这个语句,其中d.tradeid字符编码为utf8,而l.tradeid的字符编码为utf8mb4。因为utf8mb4是utf8的超集,所以MySQL在做转换时会用CONVERT将utf8转为utf8mb4。简单来看就是CONVERT作用到了d.tradeid上,因此索引失效。

    1
    select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4;

    这种情况一般有两种解决方案。

    方案1: 将关联字段的字符编码统一。

    方案2: 实在无法统一字符编码时,手动将CONVERT函数作用到关联时=的右侧,起到字符编码统一的目的,这里是强制将utf8mb4转为utf8,当然从超集向子集转换是有数据截断风险的。如下:

    1
    select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2;

    5. 对索引列进行运算,一定会导致索引失效

  • 相关阅读:
    [Codeforces Round #617 (Div. 3)] 题解 A,B,C,D,E1,E2,F
    [Codeforces Round #611 (Div. 3)] C. Friends and Gifts (随机大法好)
    [Hello 2020] D. New Year and Conference (ST表,排序)
    [Hello 2020] C. New Year and Permutation (组合数学)
    Codeforces Beta Round #7 C. Line (扩展欧几里德)
    扩展欧几里德
    Codeforces Round #349 (Div. 2) D. World Tour (最短路)
    HDU 4052 Adding New Machine (线段树+离散化)
    HDU 3265 Posters (线段树+扫描线)(面积并)
    HDU 1828 Picture (线段树+扫描线)(周长并)
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14340128.html
Copyright © 2011-2022 走看看