MySQL高级--索引失效

索引失效的场景

参考网址

索引失效的7种情况 - liehen2046 - 博客园
MySQL索引失效 - 编程柚子 - 博客园
MySQL 索引未命中情况

简介

失效场景

说明/示例

like 以%或者_开头

%和_只要不在开头,索引就有效。

or语句前后没有同时使用索引

当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效。

联合索引没遵循最左前缀

见前边的“联合索引”

数据类型出现隐式转化

varchar不加单引号的话可能会自动转换为int型,使索引无效,产生全表扫描。

例如:tb1表中name字段是字符串类型

索引有效:select * from tb1 where name = '123'

索引失效:select * from tb1 where name = 123

索引列上使用 IS NULL 或 IS NOT NULL

原因:索引无法通过空值计算出存储它的位置。

解决方法:

法1:把NULL值转为一个特定的值,在WHERE中检索时,用该特定值查找

法2:建立一个复合索引。例如:create index ind_a on table(col1,1); 通过在复合索引中指定一个非空常量值,而使构成索引的列的组合中,不可能出现全空值。

索引字段上使用not,<>,!=

不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 优化方法: key<>0 改为 key>0 or key<0。

对索引字段进行计算或者使用函数

示例:

select * from t where id*3=3000

select * from t where ABS(id)=3000

索引失效的原因是索引是针对原值建的二叉树,将列值计算后,原来的二叉树就用不上了。

order by使用错误

详见下方

全表扫描速度比索引速度快

此时mysql会使用全表扫描,索引失效。

例如:

数据量极少的表

表中索引字段数据重复率太高

where 子句中使用参数(可能失效)

可能会导致索引失效,与版本有关。

因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。此语句将进行全表扫描(可能):select id from t where num = @num
可改为强制查询使用索引:select id from t with(index(索引名)) where num = @num

 

联合索引

其他网址

联合索引(多列索引)_数据库_lm1060891265的博客-CSDN博客
mysql创建索引的原则_数据库_boss_way的博客-CSDN博客

【原创】面试官:谈谈你对mysql联合索引的认识? - 孤独烟 - 博客园

简介

  • 联合索引:对多个列进行索引,联合索引也是一棵B+树,不同的是联合索引的键值数量不是1,而是大于等于2。
  • 联合索引:遵循最左前缀(最左匹配)原则。

MySQL高级--索引失效

假定上图联合索引的为(a,b)。联合索引也是一棵B+树,不同的是B+树在对索引a排序的基础上,对索引b排序。所以数据按照(1,1), (1,2), (2,1), (2,4), (2,4), (3,1), (3,2)顺序排放。

  • a是有序的:1,1,2,2,3,3
  • b是无序的:1,2,1,4,1,2

        从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。

        因此,你执行a = 1 and b = 2是a,b字段能用到索引的。而你执行a > 1 and b = 2时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。

综上所示,最左匹配原则,在遇到范围查询的时候,就会停止匹配。

联合索引有效的情况

假设:创建联合索引:index index_name (a, b);

命令 说明
selete * from table where a=XX 会使用索引。
selete * from table where a=XX and b=YY 显然是可以使用(a,b)联合索引的
select * from table where b=YY and a=XX

        这条语句不符合最左匹配原则。但由于查询优化器的存在,MySQL优化器会自动调整where后的a,b的顺序与索引顺序一致。
        优化:在联合索引中将选择性最高的列放在索引最前面。例如:在一个公司里以age 和gender为索引,显然age要放在前面,因为性别就两种选择男或女,选择性不如age。

联合索引失效的情况

假设:创建联合索引:index index_name (a, b);

命令 说明
selete * from table where b=YY 叶子节点的b值为1,2,1,4,1,2,它不是有序的,因此不能使用(a,b)联合索引。
select * from table where a>XX and b=YY

此处只会用到a索引,不会用到b索引。

对于组合索引,会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。

比如:a = 1 and b = 2 and c > 3 and d = 4, 若建立(a,b,c,d)索引,d用不到索引。

使所有索引都有效的方案:将c放到建立索引的语句的最后,例如:建立(a,b,d,c)的索引(a,b,d的顺序可以任意调整),这样执行sql的时候,优化器会帮我们调整where后a,b,c,d的顺序(将c放到最后),让我们用上索引。

order by

其他网址

Mysql 在 order by 时索引的使用机制 - 程序员大本营
MySQL-ORDER BY索引优化_luzhensmart的专栏-CSDN博客

简介 

ORDER BY有两种实现方法:

  1. 利用有序索引自动实现。(利用有序索引的有序性而不再另做排序了(速度快))
    1. 也就是explain结果的Using index
  2. 把结果选好之后再排序。(速度慢)
    1. 也就是explain结果的Using filesort

查询和排序都走索引的情况

  1. select字段与order by字段完全相同,或者select字段与order by字段组成联合索引。
    1. 例:select col1 from tb1 order by col1;
  2. where字段与order by字段完全相同,或者where字段与order by字段组成联合索引。
    1. 例:select col1 from tb1 where col2=2 order by col2;

查询不走索引的情况

  1. select字段不包含 order by 字段。
    1. 例:select col1 from tb1 order by col2
  2. select字段包含 order by 字段 + 其他字段。
    1. 例:select col1,col3 from tb1 order by col2
  3. where 字段不包含 order by 字段。
    1. 例:select col1 from tb1 where col2=2 order by col3

实例

drop table if exists test;
create table test(
id int primary key auto_increment,
c1 varchar(10),
c2 varchar(10),
c3 varchar(10),
c4 varchar(10),
c5 varchar(10)
) ENGINE=INNODB default CHARSET=utf8;
 
insert into test(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5');
insert into test(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5');
insert into test(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5');
insert into test(c1,c2,c3,c4,c5) values('c1','b2','c7','c8','c9');
insert into test(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5');
insert into test(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5');

CREATE INDEX idx_c1234 ON test(c1,c2,c3,c4);

结果:

MySQL高级--索引失效

等值查询

案例1:SELECT *,且WHERE与ORDER BY不一样且不遵循最左前缀

EXPLAIN SELECT * FROM test WHERE c2 = 'b2' ORDER BY c1;

结果:查询不走索引;排序不走索引

MySQL高级--索引失效

 

案例2:SELECT *,且WHERE与ORDER BY不一样且遵循最左前缀

EXPLAIN SELECT * FROM test WHERE c1 = 'c1' ORDER BY c2;

结果:查询不走索引;排序不走索引

MySQL高级--索引失效

范围查询

案例1:SELECT *,且WHERE与ORDER BY不一样

EXPLAIN SELECT * FROM test WHERE c2 > 'b1' ORDER BY c1;

结果:查询不走索引;排序不走索引

MySQL高级--索引失效

案例2:SELECT 不是*,WHERE与ORDER BY不一样,ORDER BY最左前缀

EXPLAIN SELECT c3 FROM test WHERE c2 > 'b1' ORDER BY c1;

结果:查询不走索引,排序走索引。这种情况相当于只有排序使用索引

(注意:将c3换成c1,c2等,结果都是一样的)

MySQL高级--索引失效

案例3:SELECT *,WHERE与ORDER BY一样且遵循最左

EXPLAIN SELECT * FROM test WHERE c1 > 'b1' ORDER BY c1;

结果:查询走索引;排序不用操作,直接用查询的结果即可

MySQL高级--索引失效

案例4:SELECT *,WHERE与ORDER BY不一样且遵循最左

EXPLAIN SELECT * FROM test WHERE c1 > 'b1' ORDER BY c2;

结果:查询走索引;排序不走索引

MySQL高级--索引失效

like模糊查询

参考网址

MySQL - 如何优化模糊查询(like 模糊查询)_数据库_Soinice的博客-CSDN博客
【转】【MySQL】Mysql模糊查询like提速优化 - 梦琪小生 - 博客园
SQL使用模糊查询like的优化 - 简书
索引与优化like查询

简介

        在MySQL中,模糊查询肯定要使用like关键字,然后再加 %,是代表前模糊还是后模糊。数据量小的情况下,不容易看出查询的效率,但是数据量达到百万级,千万级甚至更高的时候,查询的效率就很容易显现出来了。

        本处假设要查询的字段(where子句)都已经加了索引。

索引是否失效

解决方法

col_name like 'ABC%'

索引有效

 

col_name like '%ABC'

索引失效,使用全表扫描

翻转函数+like前模糊查询+建立翻转函数索引=走翻转函数索引

col_name like '%ABC%'

索引失效

locate, position,instr,find_in_set等方法

col_name like '%ABC'

本语句不走索引:

select object_name from t1 where object_name like '%ABC';

解决方法:

create index idx_t1_objectname2 on t1(reverse(object_name));
select object_name from t1 where reverse(object_name) like reverse('%ABC');

看执行计划:(改写后SQL走了索引。)

MySQL高级--索引失效

col_name like '%ABC%'

有三种情况:
1、ABC在字符串中位置不固定,可以通过改写SQL进行优化
2、ABC始终从字符串开始的某个固定位置出现,可以创建函数索引进行优化
3、ABC始终从字符串结尾的某个固定位置出现,可以创建函数组合索引进行优化

情况1

函数

作用

示例/说明

LOCATE(substr,str)

返回 substr 在 str 中第一次出现的位置,如果 substr 在 str 中不存在,返回值为 0 。

如果str是字段,则查找字段内容,如果str不是字段,则将str作为要查的库。

SELECT LOCATE('xbar',`foobar`);   #返回0

#若`foobar`是字段,则去此字段查询,而不是”foobar”字符串

 

SELECT LOCATE('bar',`foobarbar`); #返回4

SELECT `column` FROM `table` WHERE LOCATE('keyword', `field`)>0

LOCATE(substr,str, pos)

返回 substr 在 str 第pos个位置后第一次出现的位置,如果 substr 在 str 中不存在,返回值为0。

SELECT LOCATE('bar',`foobarbar`,5); #返回7

 

POSITION('substr' IN `field`)

功能跟locate一样

SELECT `col` FROM `tb1` WHERE POSITION('keyword' IN `filed`)

SELECT `column` FROM `table` WHERE POSITION('keyword' IN `filed`)

FIND_IN_SET(str,strlist)

查询字段(strlist)中包含(str)的结果,返回记录(没有则返回null)

 

str 要查询的字符串

strlist 字段名 参数以”,”分隔 如 (1,2,6,8,10,22)

SELECT FIND_IN_SET('b', 'a,b,c,d'); #返回2

 

SELECT name,role FROM `user` WHERE find_in_set('2',role)

 

find_in_set()和like的区别

主要的区别就是like是广泛的模糊查询,而 find_in_set() 是精确匹配,并且字段值之间用‘,'分开。

 

SELECT userid,username,userrole 角色 FROM `user` WHERE userrole LIKE '%2%';

 

SELECT userid,username,userrole 角色 FROM `user` WHERE find_in_set('2',userrole)

情况2

先创建substr函数索引,再使用like ‘ABC%’。

假如ABC从字符串第五位出现:

create index idx_substr_t1_objname on t1 (substr(object_name,5,30));
select object_id,object_type,object_name from t1
where substr(object_name,5,30) like 'TAB%';

情况3:

查询%xx的记录

select count(c.c_ply_no) as COUNT
  from Policy_Data_All c, Item_Data_All i
  where c.c_ply_no = i.c_ply_no
  and i.C_LCN_NO like '%245'

在执行的时候,执行计划显示,消耗值,io值,cpu值均非常大,原因是like后面前模糊查询导致索引失效,进行全表扫描。

解决方法:这种只有前模糊的sql可以改造如下写法

select count(c.c_ply_no) as COUNT
  from Policy_Data_All c, Item_Data_All i
  where c.c_ply_no = i.c_ply_no
  and reverse(i.C_LCN_NO) like reverse('%245')

优化原理

        用索引全扫描取代表的全扫描。因为索引全扫描的代价是全表扫描的1/N (即索引块数与数据块数的比例),表越大,优化效果越明显。
        改写后SQL的执行计划,根据索引再回表的代价要看符合条件的记录数多少:如果in子查询返回的记录数很少,那么优化的效果就相当于效率提高了N倍;如果in子查询返回的记录数较多,两种SQL的性能区别就不是很明显了

上一篇:MySQL中的LIKE关键字模糊查询条件拼接


下一篇:微笑到颠笑,各种笑不停的英语说法