• 面试官:讲讲MySql索引失效的几种情况


    索引失效#

    准备数据:#

    CREATE TABLE `dept` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`deptName` VARCHAR(30) DEFAULT NULL,
    	`address` VARCHAR(40) DEFAULT NULL,
    	ceo INT NULL ,
    	PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=1;
    
    CREATE TABLE `emp` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`empno` INT NOT NULL ,
    	`name` VARCHAR(20) DEFAULT NULL,
    	`age` INT(3) DEFAULT NULL,
    	`deptId` INT(11) DEFAULT NULL,
    	PRIMARY KEY (`id`)
    	#CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=1;
    

    1、计算、函数导致索引失效#

    -- 显示查询分析
    EXPLAIN SELECT * FROM emp WHERE emp.name  LIKE 'abc%';
    EXPLAIN SELECT * FROM emp WHERE LEFT(emp.name,3) = 'abc'; --索引失效
    

    image-20220710212756603

    2、LIKE以%开头索引失效#

    EXPLAIN SELECT * FROM emp WHERE name LIKE '%ab%'; --索引失效
    

    image-20220710213641577

    拓展:Alibaba《Java开发手册》

    【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

    3、不等于(!= 或者<>)索引失效#

    EXPLAIN SELECT * FROM emp WHERE emp.name = 'abc' ;
    EXPLAIN SELECT * FROM emp WHERE emp.name <> 'abc' ; --索引失效
    

    image-20220710214317491

    4、IS NOT NULL 和 IS NULL#

    EXPLAIN SELECT * FROM emp WHERE emp.name IS NULL;
    EXPLAIN SELECT * FROM emp WHERE emp.name IS NOT NULL; --索引失效
    

    image-20220710214840703

    注意:当数据库中的数据的索引列的NULL值达到比较高的比例的时候,即使在IS NOT NULL 的情况下 MySQL的查询优化器会选择使用索引,此时type的值是range(范围查询)

    -- 将 id>20000 的数据的 name 值改为 NULL
    UPDATE emp SET `name` = NULL WHERE `id` > 20000;
    
    -- 执行查询分析,可以发现 IS NOT NULL 使用了索引
    -- 具体多少条记录的值为NULL可以使索引在IS NOT NULL的情况下生效,由查询优化器的算法决定
    EXPLAIN SELECT * FROM emp WHERE emp.name IS NOT NULL;
    

    image-20220711175040606

    测试完将name的值改回来

    UPDATE emp SET `name` = rand_string(6) WHERE `id` > 20000;
    

    5、类型转换导致索引失效#

    EXPLAIN SELECT * FROM emp WHERE name='123'; 
    EXPLAIN SELECT * FROM emp WHERE name= 123; --索引失效
    

    image-20220710215217274

    6、全值匹配我最爱#

    准备:

    -- 首先删除之前创建的索引
    CALL proc_drop_index("atguigudb","emp");
    

    问题:为以下查询语句创建哪种索引效率最高

    -- 查询分析
    EXPLAIN SELECT * FROM emp WHERE emp.age = 30 and deptid = 4 AND emp.name = 'abcd';
    -- 执行SQL
    SELECT * FROM emp WHERE emp.age = 30 and deptid = 4 AND emp.name = 'abcd';
    -- 查看执行时间
    SHOW PROFILES;
    

    创建索引并重新执行以上测试:

    -- 创建索引:分别创建以下三种索引的一种,并分别进行以上查询分析
    CREATE INDEX idx_age ON emp(age);
    CREATE INDEX idx_age_deptid ON emp(age,deptid);
    CREATE INDEX idx_age_deptid_name ON emp(age,deptid,`name`);
    

    结论:可以发现最高效的查询应用了联合索引 idx_age_deptid_name

    image-20220711190731030

    7、最佳左前缀法则#

    准备:

    -- 首先删除之前创建的索引
    CALL proc_drop_index("atguigudb","emp");
    -- 创建索引
    CREATE INDEX idx_age_deptid_name ON emp(age,deptid,`name`);
    

    问题:以下这些SQL语句能否命中 idx_age_deptid_name 索引,可以匹配多少个索引字段

    测试:

    • 如果索引了多列,要遵守最左前缀法则。即查询从索引的最左前列开始并且不跳过索引中的列。
    • 过滤条件要使用索引,必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。
    EXPLAIN SELECT * FROM emp WHERE emp.age=30 AND emp.name = 'abcd' ;
    -- EXPLAIN结果:
    -- key_len:5 只使用了age索引
    -- 索引查找的顺序为 age、deptid、name,查询条件中不包含deptid,无法使用deptid和name索引
    
    EXPLAIN SELECT * FROM emp WHERE emp.deptid=1 AND emp.name = 'abcd';
    -- EXPLAIN结果:
    -- type: ALL, 执行了全表扫描
    -- key_len: NULL, 索引失效
    -- 索引查找的顺序为 age、deptid、name,查询条件中不包含age,无法使用整个索引
    
    EXPLAIN SELECT * FROM emp WHERE emp.age = 30 AND emp.deptid=1 AND emp.name = 'abcd';
    -- EXPLAIN结果:
    -- 索引查找的顺序为 age、deptid、name,匹配所有索引字段
    
    EXPLAIN SELECT * FROM emp WHERE emp.deptid=1 AND emp.name = 'abcd' AND emp.age = 30;
    -- EXPLAIN结果:
    -- 索引查找的顺序为 age、deptid、name,匹配所有索引字段
    
    

    8、索引中范围条件右边的列失效#

    准备:

    -- 首先删除之前创建的索引
    CALL proc_drop_index("atguigudb","emp");
    

    问题:为以下查询语句创建哪种索引效率最高

    EXPLAIN SELECT * FROM emp WHERE emp.age=30 AND emp.deptId>1000 AND emp.name = 'abc'; 
    

    测试1:

    -- 创建索引并执行以上SQL语句的EXPLAIN
    CREATE INDEX idx_age_deptid_name ON emp(age,deptid,`name`);
    -- key_len:10, 只是用了 age 和 deptid索引,name失效
    

    注意:当我们修改deptId的范围条件的时候,例如deptId>100,那么整个索引失效,MySQL的优化器基于成本计算后认为没必要使用索引了,所以就进行了全表扫描。(注意:因为表中的数据是随机生成的,因此实际测试中根据具体数据的不同测试的结果也会不一样,最终是否使用索引由优化器决定)

    image-20220711215826013

    测试2:

    -- 创建索引并执行以上SQL语句的EXPLAIN(将deptid索引的放在最后)
    CREATE INDEX idx_age_name_deptid ON emp(age,`name`,deptid);
    -- 使用了完整的索引
    

    image-20220712080350120

    补充:以上两个索引都存在的时候,MySQL优化器会自动选择最好的方案

  • 相关阅读:
    载阿霉素PLGA-PLL-PEG纳米粒|马钱子碱mPEG-PLGA纳米粒|PLGA-PEG包裹液态PFP载紫杉醇纳米粒
    esp32 桌面小电视项目(基于freertos)(六)
    鲜花线上销售管理系统的设计与实现
    C练题笔记之:Leetcode-137. 只出现一次的数字 II
    DeepFace【部署 02】轻量级人脸识别和面部属性分析框架(实时分析+API+Docker部署+命令行接口)
    Timesnet: Temporal 2d-variation modeling for general time series analysis
    elementUI的table使用展开功能( type=“expand“ ),展开时合起上一次展开的内容,始终保持展开内容为一个,并且再次点击合起自身
    使用html+css+js实现一个静态页面(含源码)
    npm与包
    shell程序中常见的正规表达式
  • 原文地址:https://www.cnblogs.com/deyo/p/17514052.html