SQL索引失效的11种情况详析

目录
  • 索引失效案例
    • 【1】. 全值匹配
    • 【2】. 最佳左前缀法则
    • 【3】. 主键插入顺序
    • 【4】. 计算、函数、类型转换(自动或手动)导致索引失效
    • 【5】. 类型转换导致索引失效
    • 【6】. 范围条件右边的列索引失效
    • 【7】. 不等于(!= 或者<>)索引失效
    • 【8】. is null可以使用索引,is not null无法使用索引
    • 【9】. like以通配符%开头索引失效
    • 【10】. OR 前后存在非索引的列,索引失效
    • 【11】. 数据库和表的字符集统一使用utf8mb4
  • 总结

数据库调优的大致方向:

  • 索引失效,没有充分利用到索引——建立索引
  • 关联查询太多join——sql优化
  • 服务器调优及各个参数设置——my.cnf
  • 数据过多——分库分表

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

  • 物理查询优化:通过索引和表连接方式等技术进行优化
  • 逻辑查询优化:通过SQL等价变换提升查询效率,就是换一种sql写法

数据准备:

CREATE DATABASE atguigudb2;
USE atguigudb2;

#############    class 表    #################
CREATE TABLE `class` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`className` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(40) DEFAULT NULL,
`monitor` INT NULL ,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

#############    student 表    #################
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`stuno` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`classId` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
#CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

#################################

SET GLOBAL log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。

#随机产生字符串
DELIMITER //
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
#假如要删除
#drop function rand_string;

#用于随机产生多少到多少的编号
DELIMITER //
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
RETURN i;
END //
DELIMITER ;
#假如要删除
#drop function rand_num;

#创建往stu表中插入数据的存储过程
DELIMITER //
CREATE PROCEDURE insert_stu( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO student (stuno, NAME ,age ,classId ) VALUES
((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;

#执行存储过程,往class表添加随机数据
DELIMITER //
CREATE PROCEDURE `insert_class`( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO class ( classname,address,monitor ) VALUES
(rand_string(8),rand_string(10),rand_num(1,100000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;

#执行存储过程,往class表添加1万条数据
CALL insert_class(10000);

#执行存储过程,往stu表添加50万条数据
CALL insert_stu(100000,500000);

SELECT COUNT(*) FROM class;
SELECT COUNT(*) FROM student;

############################### 删除索引的存储过程 ########################
DELIMITER //
CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE ct INT DEFAULT 0;
DECLARE _index VARCHAR(200) DEFAULT '';
DECLARE _cur CURSOR FOR SELECT index_name FROM
information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND
seq_in_index=1 AND index_name <>'PRIMARY' ;
#每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=2 ;
#若没有数据返回,程序继续,并将变量done设为2
OPEN _cur;
FETCH _cur INTO _index;
WHILE _index<>'' DO
SET @str = CONCAT("drop index " , _index , " on " , tablename );
PREPARE sql_str FROM @str ;
EXECUTE sql_str;
DEALLOCATE PREPARE sql_str;
SET _index='';
FETCH _cur INTO _index;
END WHILE;
CLOSE _cur;
END //
DELIMITER ;
# 执行存储过程
CALL proc_drop_index("dbname","tablename");

索引失效案例

【1】. 全值匹配

# 【1】. 全值匹配
# student表,主键id,此时无索引,耗时大
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4 AND NAME = 'abcd';

# 注:SQL_NO_CACHE 不使用查询缓存

# 建立索引
CREATE INDEX idx_age ON student(age);
CREATE INDEX idx_age_classid ON student(age,classId);
CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);
# 此时第三条查询语句默认使用最后一条索引,而不是前两个

【2】. 最佳左前缀法则

# 【2】. 最佳左前缀法则
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age = 30 AND student.name = 'abcd';
# 查age&name,用age的索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid = 1 AND student.name = 'abcd';
# 查classid&name,classid在前,有索引的话先找classid相同的,再找name,
#但现在没有这样的索引,idx_age_classid_name的字段顺序是先找age,所以不符合,所以此时不能用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE classid = 4 AND student.age = 30 AND student.name = 'abcd';
#idx_age_classid_name 联合索引中所有字段均出现,可以使用该索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age = 30 AND student.name = 'abcd';
# 现在,删除idx_age和idx_age_classid,发现用到idx_age_classid_name,而key_len=5,即只用到age字段,int(4)+null(1)
#因为索引完age后没有classid了,不能再查找到name

【3】. 主键插入顺序

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

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

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

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

# 【4】. 计算、函数、类型转换(自动或手动)导致索引失效
##### 例1:
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';	#更好,能够使用上索引
# type=range 使用了索引中的排序

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';	# left(text,num_chars):截取左侧n个字符
# type = all 全表的访问
# 该语句的执行过程:针对每一条数据,一个一个取出,先作用一遍函数,再拿函数结果与abc对比,用不上b+树

CREATE INDEX idx_name ON student(NAME);

##### 例2:
CREATE INDEX idx_sno ON student(stuno);
EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno+1 = 900001; 	# type = all 需要做运算,无法直接用索引找值

EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno = 900000; 	# type = ref

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

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

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

# 【6】. 范围条件右边的列索引失效 ( > < >= <= between 等)
SHOW INDEX FROM student;
CALL proc_drop_index('atguigudb2','student');

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age = 30 AND student.classId > 20 AND student.name = 'abc';	# 这三个and先写谁无所谓,优化器会调优
# key_len = 10, age=5,classId=5,name用不上。classId 是范围,索引右侧的name用不上

# 改写索引:
CREATE INDEX idx_age_name_cid ON student(age,NAME,classId); 	#把需要排序的classid放到最后
# 此时在执行上面的语句,就使用了这个索引,key_len=73

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

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

# 【7】. 不等于(!= 或者<>)索引失效
CREATE INDEX idx_name ON student(NAME);

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc';	# 索引失效 索引查的是等于

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

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

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

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

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE '%ab';	# type = all 索引失效

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

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

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

CALL proc_drop_index('atguigudb2','student');
SHOW INDEX FROM student;
# 创建一个age的索引
CREATE INDEX idx_age ON student(age);

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;	# 未使用索引,索引+全表扫描->全表扫描
# 再加一个字段的单独索引
CREATE INDEX idx_cid ON student(classid);
# 再执行上条语句,此时 type = index_merge ,key = idx_age,idx_cid。

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

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

总结

到此这篇关于SQL索引失效的11种情况详析的文章就介绍到这了,更多相关SQL索引失效内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL导致索引失效的几种情况

    目录 一.准备工作 二.索引失效规则 1.优先使用联合索引 2.最左匹配原则 3.范围条件右边的列索引失效 4.计算.函数导致索引失效 5.类型转换导致索引失效 6.不等于(!= 或者<>)索引失效 7.is null可以使用索引,is not null无法使用索引 8.like以%开头,索引失效 9.OR前后存在非索引的列,索引失效 10.字符集不统一 三.建议 一.准备工作 首先准备两张表用于演示: CREATE TABLE `student_info` ( `id` int NOT NU

  • mysql 联合索引生效的条件及索引失效的条件

    目录 1.联合索引失效的条件 2.索引失效的条件 1.联合索引失效的条件 联合索引又叫复合索引.两个或更多个列上的索引被称作复合索引. 对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分.例如索引是key index (a,b,c). 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效. 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单

  • MySQL中索引失效的常见场景与规避方法

    前言 之前有看过许多类似的文章内容,提到过一些sql语句的使用不当会导致MySQL的索引失效.还有一些MySQL"军规"或者规范写明了某些sql不能这么写,否则索引失效. 绝大部分的内容笔者是认可的,不过部分举例中笔者认为用词太绝对了,并没有说明其中的原由,很多人不知道为什么.所以笔者绝对再整理一遍MySQL中索引失效的常见场景,并分析其中的原由供大家参考. 当然请记住,explain是一个好习惯! MySQL索引失效的常见场景 在验证下面的场景时,请准备足够多的数据量,因为数据量少时

  • MySQL索引失效原理

    目录 1.索引失效原因 2.再来看看哪些情况会破坏索引的有序性. - 对索引字段做函数操作 - 隐式类型转换 - 隐式字符编码转换 3.总结 1.索引失效原因 首先看看哪些情况下,将会导致查找不能利用索引的有序性. 假设一个表test中有a,b,c,d四个字段,c是主键. 在a,b字段上建立联合索引(a,b):CREATE index idx_a_b on test(a,b); B+树联合索引.JPG 可以得到的规律是:优先按a字段从小到大排序,a字段相等的按b字段从小到大排序: 分析以下情况,

  • 解决mysql模糊查询索引失效问题的几种方法

    我们在使用like %通配符时常常会引起索引失效的问题. 这里,我们讨论一下like使用%的几种情况: 下列例子用到的索引(VC_STUDENT_NAME) 一.like 'xx%' EXPLAIN select * from t_student where VC_STUDENT_NAME like '王%' 我们发现使用%不放在开头的时候,索引是有效的 二.like '%xx' EXPLAIN select * from t_student where VC_STUDENT_NAME like

  • Mysql索引会失效的几种情况分析

    索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引 此外,查看索引的使用情况show status li

  • mysql索引失效的五种情况分析

    索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引 此外,查看索引的使用情况 show status lik

  • MySQL中有哪些情况下数据库索引会失效详析

    前言 要想分析MySQL查询语句中的相关信息,如是全表查询还是部分查询,就要用到explain. 索引的优点 大大减少了服务器需要扫描的数据量 可以帮助服务器避免排序或减少使用临时表排序 索引可以随机I/O变为顺序I/O 索引的缺点 需要占用磁盘空间,因此冗余低效的索引将占用大量的磁盘空间 降低DML性能,对于数据的任意增删改都需要调整对应的索引,甚至出现索引分裂 索引会产生相应的碎片,产生维护开销 一.explain 用法:explain +查询语句. id:查询语句的序列号,上面图片中只有一

  • mysql的in会不会让索引失效?

    mysql的in会让索引失效吗?不会! 看结果: mysql> desc select * from tb_province where name in ('lily3', 'lily2', 'lily1'); +----+-------------+-------------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_t

  • MySQL索引失效的几种情况汇总

    一.索引不存储null值 更准确的说,单列索引不存储null值,复合索引不存储全为null的值.索引不能存储Null,所以对这列采用is null条件时,因为索引上根本 没Null值,不能利用到索引,只能全表扫描. 为什么索引列不能存Null值? 将索引列值进行建树,其中必然涉及到诸多的比较操作.Null值的特殊性就在于参与的运算大多取值为null. 这样的话,null值实际上是不能参与进建索引的过程.也就是说,null值不会像其他取值一样出现在索引树的叶子节点上. 二.不适合键值较少的列(重复

随机推荐