SQL索引失效的情况有哪些

数据库   发布日期:2023年08月12日   浏览次数:512

这篇文章主要介绍了SQL索引失效的情况有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SQL索引失效的情况有哪些文章都会有所收获,下面我们一起来看看吧。

    数据库调优的大致方向:

    • 索引失效,没有充分利用到索引——建立索引

    • 关联查询太多join——sql优化

    • 服务器调优及各个参数设置——my.cnf

    • 数据过多——分库分表

    sql查询优化技术有很多,大体分为物理查询优化逻辑查询优化:

    • 物理查询优化:通过索引和表连接方式等技术进行优化

    • 逻辑查询优化:通过SQL等价变换提升查询效率,就是换一种sql写法

    数据准备:

    1. CREATE DATABASE atguigudb2;
    2. USE atguigudb2;
    3. ############# class 表 #################
    4. CREATE TABLE `class` (
    5. `id` INT(11) NOT NULL AUTO_INCREMENT,
    6. `className` VARCHAR(30) DEFAULT NULL,
    7. `address` VARCHAR(40) DEFAULT NULL,
    8. `monitor` INT NULL ,
    9. PRIMARY KEY (`id`)
    10. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    11. ############# student 表 #################
    12. CREATE TABLE `student` (
    13. `id` INT(11) NOT NULL AUTO_INCREMENT,
    14. `stuno` INT NOT NULL ,
    15. `name` VARCHAR(20) DEFAULT NULL,
    16. `age` INT(3) DEFAULT NULL,
    17. `classId` INT(11) DEFAULT NULL,
    18. PRIMARY KEY (`id`)
    19. #CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
    20. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    21. #################################
    22. SET GLOBAL log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。
    23. #随机产生字符串
    24. DELIMITER //
    25. CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
    26. BEGIN
    27. DECLARE chars_str VARCHAR(100) DEFAULT
    28. 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
    29. DECLARE return_str VARCHAR(255) DEFAULT '';
    30. DECLARE i INT DEFAULT 0;
    31. WHILE i < n DO
    32. SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
    33. SET i = i + 1;
    34. END WHILE;
    35. RETURN return_str;
    36. END //
    37. DELIMITER ;
    38. #假如要删除
    39. #drop function rand_string;
    40. #用于随机产生多少到多少的编号
    41. DELIMITER //
    42. CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
    43. BEGIN
    44. DECLARE i INT DEFAULT 0;
    45. SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
    46. RETURN i;
    47. END //
    48. DELIMITER ;
    49. #假如要删除
    50. #drop function rand_num;
    51. #创建往stu表中插入数据的存储过程
    52. DELIMITER //
    53. CREATE PROCEDURE insert_stu( START INT , max_num INT )
    54. BEGIN
    55. DECLARE i INT DEFAULT 0;
    56. SET autocommit = 0; #设置手动提交事务
    57. REPEAT #循环
    58. SET i = i + 1; #赋值
    59. INSERT INTO student (stuno, NAME ,age ,classId ) VALUES
    60. ((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000));
    61. UNTIL i = max_num
    62. END REPEAT;
    63. COMMIT; #提交事务
    64. END //
    65. DELIMITER ;
    66. #执行存储过程,往class表添加随机数据
    67. DELIMITER //
    68. CREATE PROCEDURE `insert_class`( max_num INT )
    69. BEGIN
    70. DECLARE i INT DEFAULT 0;
    71. SET autocommit = 0;
    72. REPEAT
    73. SET i = i + 1;
    74. INSERT INTO class ( classname,address,monitor ) VALUES
    75. (rand_string(8),rand_string(10),rand_num(1,100000));
    76. UNTIL i = max_num
    77. END REPEAT;
    78. COMMIT;
    79. END //
    80. DELIMITER ;
    81. #执行存储过程,往class表添加1万条数据
    82. CALL insert_class(10000);
    83. #执行存储过程,往stu表添加50万条数据
    84. CALL insert_stu(100000,500000);
    85. SELECT COUNT(*) FROM class;
    86. SELECT COUNT(*) FROM student;
    87. ############################### 删除索引的存储过程 ########################
    88. DELIMITER //
    89. CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
    90. BEGIN
    91. DECLARE done INT DEFAULT 0;
    92. DECLARE ct INT DEFAULT 0;
    93. DECLARE _index VARCHAR(200) DEFAULT '';
    94. DECLARE _cur CURSOR FOR SELECT index_name FROM
    95. information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND
    96. seq_in_index=1 AND index_name <>'PRIMARY' ;
    97. #每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束
    98. DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=2 ;
    99. #若没有数据返回,程序继续,并将变量done设为2
    100. OPEN _cur;
    101. FETCH _cur INTO _index;
    102. WHILE _index<>'' DO
    103. SET @str = CONCAT("drop index " , _index , " on " , tablename );
    104. PREPARE sql_str FROM @str ;
    105. EXECUTE sql_str;
    106. DEALLOCATE PREPARE sql_str;
    107. SET _index='';
    108. FETCH _cur INTO _index;
    109. END WHILE;
    110. CLOSE _cur;
    111. END //
    112. DELIMITER ;
    113. # 执行存储过程
    114. CALL proc_drop_index("dbname","tablename");

    索引失效案例

    【1】. 全值匹配

    1. # 【1】. 全值匹配
    2. # student表,主键id,此时无索引,耗时大
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30;
    4. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4;
    5. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4 AND NAME = 'abcd';
    6. # 注:SQL_NO_CACHE 不使用查询缓存
    7. # 建立索引
    8. CREATE INDEX idx_age ON student(age);
    9. CREATE INDEX idx_age_classid ON student(age,classId);
    10. CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);
    11. # 此时第三条查询语句默认使用最后一条索引,而不是前两个

    【2】. 最佳左前缀法则

    1. # 【2】. 最佳左前缀法则
    2. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age = 30 AND student.name = 'abcd';
    3. # 查age&name,用age的索引
    4. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid = 1 AND student.name = 'abcd';
    5. # 查classid&name,classid在前,有索引的话先找classid相同的,再找name,
    6. #但现在没有这样的索引,idx_age_classid_name的字段顺序是先找age,所以不符合,所以此时不能用索引
    7. EXPLAIN SELECT SQL_NO_CACHE * FROM student
    8. WHERE classid = 4 AND student.age = 30 AND student.name = 'abcd';
    9. #idx_age_classid_name 联合索引中所有字段均出现,可以使用该索引
    10. EXPLAIN SELECT SQL_NO_CACHE * FROM student
    11. WHERE student.age = 30 AND student.name = 'abcd';
    12. # 现在,删除idx_age和idx_age_classid,发现用到idx_age_classid_name,而key_len=5,即只用到age字段,int(4)+null(1)
    13. #因为索引完age后没有classid了,不能再查找到name

    【3】. 主键插入顺序

    在定义表时,让主键auto_increment,否则,插入一条数据时可能会移动大量数据。

    如,往 1 5 8 10 15 &hellip; 100 中插9,会放在8 10 中间,因为索引默认升序排列。那么10往后的数据都要挪动,页不够时又要放到下一页,每插一条数据都这样挪一次,开销很大

    我们自定义的主键列id 拥有AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。

    【4】. 计算、函数、类型转换(自动或手动)导致索引失效

    1. # 【4】. 计算、函数、类型转换(自动或手动)导致索引失效
    2. ##### 例1:
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%'; #更好,能够使用上索引
    4. # type=range 使用了索引中的排序
    5. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc'; # left(text,num_chars):截取左侧n个字符
    6. # type = all 全表的访问
    7. # 该语句的执行过程:针对每一条数据,一个一个取出,先作用一遍函数,再拿函数结果与abc对比,用不上b+树
    8. CREATE INDEX idx_name ON student(NAME);
    9. ##### 例2:
    10. CREATE INDEX idx_sno ON student(stuno);
    11. EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno+1 = 900001; # type = all 需要做运算,无法直接用索引找值
    12. EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno = 900000; # type = ref

    【5】. 类型转换导致索引失效

    1. # 【5】. 类型转换导致索引失效
    2. # 未使用到索引
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME=123; # 这里使用了隐式转换
    4. # 使用到索引
    5. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME='123'; # name本身就是字符串类型

    【6】. 范围条件右边的列索引失效

    1. # 【6】. 范围条件右边的列索引失效 ( > < >= <= between 等)
    2. SHOW INDEX FROM student;
    3. CALL proc_drop_index('atguigudb2','student');
    4. CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);
    5. EXPLAIN SELECT SQL_NO_CACHE * FROM student
    6. WHERE student.age = 30 AND student.classId > 20 AND student.name = 'abc'; # 这三个and先写谁无所谓,优化器会调优
    7. # key_len = 10, age=5,classId=5,name用不上。classId 是范围,索引右侧的name用不上
    8. # 改写索引:
    9. CREATE INDEX idx_age_name_cid ON student(age,NAME,classId); #把需要排序的classid放到最后
    10. # 此时在执行上面的语句,就使用了这个索引,key_len=73

    创建的联合索引中,必须把涉及到范围的字段写在最后。

    【7】. 不等于(!= 或者<>)索引失效

    1. # 【7】. 不等于(!= 或者<>)索引失效
    2. CREATE INDEX idx_name ON student(NAME);
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc'; # 索引失效 索引查的是等于

    【8】. is null可以使用索引,is not null无法使用索引

    1. # 【8】. is null可以使用索引,is not null无法使用索引
    2. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL; # type=ref 相当于等于某个值
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL; # 索引失效 相当于不等于

    【9】. like以通配符%开头索引失效

    1. # 【9】. like以通配符%开头索引失效
    2. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE 'ab%'; # 可用索引
    3. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE '%ab'; # type = all 索引失效

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

    【10】. OR 前后存在非索引的列,索引失效

    1. # 【10】. OR 前后存在非索引的列,索引失效
    2. CALL proc_drop_index('atguigudb2','student');
    3. SHOW INDEX FROM student;
    4. # 创建一个age的索引
    5. CREATE INDEX idx_age ON student(age);
    6. EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100; # 未使用索引,索引+全表扫描->全表扫描
    7. # 再加一个字段的单独索引
    8. CREATE INDEX idx_cid ON student(classid);
    9. # 再执行上条语句,此时 type = index_merge ,key = idx_age,idx_cid。

    【11】. 数据库和表的字符集统一使用utf8mb4

    统一使用utf8mb4( 5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。不同的字符集进行比较前需要进行转换会造成索引失效。

    以上就是SQL索引失效的情况有哪些的详细内容,更多关于SQL索引失效的情况有哪些的资料请关注九品源码其它相关文章!