MySQL模糊查询详解

一时失言乱红尘 2023-09-30 14:07 101阅读 0赞

单列模糊查询

使用的表结构

  1. CREATE TABLE `user` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `name` varchar(32) DEFAULT NULL COMMENT '姓名',
  4. `city` varchar(32) DEFAULT NULL COMMENT '城市',
  5. `age` int(11) DEFAULT NULL COMMENT '年龄',
  6. PRIMARY KEY (`id`),
  7. KEY `idx_name_city` (`name`,`city`),
  8. KEY `idx_city` (`city`) USING BTREE
  9. ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
  10. insert into user(name, city, age) values("ZhaoDa", "BeiJing", 20),("QianEr", "ShangHai", 21),("SunSan", "GuanZhou", 22), ("LiSi", "ShenZhen", 24), ("ZhouWu", "NingBo", 25), ("WuLiu", "HangZhou", 26), ("ZhengQi", "NanNing", 27), ("WangBa", "YinChuan", 28), ("LiSi", "TianJin", 29), ("ZhangSan", "NanJing", 30), ("CuiShi", "ZhengZhou", 65), ("LiSi", "KunMing", 29), ("LiSi", "ZhengZhou", 30);

e4a5764c03014d6d93b489d2c94636f7.png

执行以下三条SQL

  1. explain select * from user where city like '%h';

76d0535217704d7094c895127e2530ad.png

  1. explain select * from user where city like 'h%';

966e61b7b1b841a8a5ba4682cb6d8d0b.png

  1. explain select * from user where city like '%h%';

557667256b2e4a90962a8f08272d92c6.png

从执行结果上看只有第二条sql的索引没有失效,

结论:如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

单列模糊查询优化

对于如下SQL

  1. explain select * from user where city like '%h%'

从执行计划看到 type=ALL,Extra=Using where 走的是全部扫描,没有利用到ICP特性。辅助索引idx_city(city)内部是包含主键id的,等价于(id,city)的复合索引,尝试利用覆盖索引特性

  1. explain select id from user where city like '%h%';

c801d4b9a629414697e4db72817e5c66.png

从执行计划看到,type=index,Extra=Using where; Using index,索引全扫描,但是需要的数据都在索引列中能找到,不需要回表。利用这个特点,将原始的SQL语句先获取主键id,然后通过id跟原表进行关联,分析其执行计划。

  1. explain select u.* from user u , (select id from user where city like '%h%') t where u.id = t.id;

c24dc7743fe743db86ad5efd8aa4aadd.png

从执行计划看,走了索引idx_city(city),不需要回表访问数据,执行时间从60ms降低为40ms,type = index 说明没有用到ICP特性,但是可以利用 Using where; Using index 这种索引扫描不回表的方式减少资源开销来提升性能。

联合索引模糊查询

索引条件下推ICP

ICP介绍

MySQL 5.6开始支持ICP(Index Condition Pushdown),不支持ICP之前,当进行索引查询时,首先根据索引来查找数据,然后再根据where条件来过滤,扫描了大量不必要的数据,增加了数据库IO操作。

在支持ICP后,MySQL在取出索引数据的同时,判断是否可以进行where条件过滤,将where的部分过滤操作放在存储引擎层提前过滤掉不必要的数据,减少了不必要数据被扫描带来的IO开销。

在某些查询下,可以减少Server层对存储引擎层数据的读取,从而提供数据库的整体性能。

官方文档:MySQL :: MySQL 8.0 Reference Manual :: 8.2.1.6 Index Condition Pushdown Optimization

ICP具有以下特点

0f67b126324d4f5eb82b3ea91e87578b.png

原理

为了理解ICP是如何工作的,我们先了解下没有使用ICP的情况下,MySQL是如何查询的:

  • 存储引擎读取索引记录;
  • 根据索引中的主键值,定位并读取完整的行记录;
  • 存储引擎把记录交给Server层去检测该记录是否满足WHERE条件。

使用ICP的情况下,查询过程如下:

  • 读取索引记录(不是完整的行记录);
  • 判断WHERE条件部分能否用索引中的列来做检查,条件不满足,则处理下一行索引记录;
  • 条件满足,使用索引中的主键去定位并读取完整的行记录(就是所谓的回表);
  • 存储引擎把记录交给Server层,Server层检测该记录是否满足WHERE条件的其余部分。

实践

  1. set optimizer_switch = 'index_condition_pushdown=off';
  2. explain select * from user where name="LiSi" and city like "%Z%" and age > 25;

83df0083213248809e842faf59cf5fe7.png

在不使用索引下推的情况下,根据联合索引“最左匹配”原则,只有name列能用到索引,city列由于是模糊匹配,是不能用到索引的,此时的执行过程是这样的:

59138f2e243b4582bbfee543a0325261.png

使用索引下推

  1. set optimizer_switch = 'index_condition_pushdown=on';
  2. explain select * from user where name="LiSi" and city like "%Z%" and age > 25;

5310f7724c904105bf34f699e3b03309.png

19d39a7ccf1f4909887d0f1c57e11f58.png

  • 存储引擎根据(name, city)联合索引,找到name='LiSi'的记录,共4条;

  • 由于联合索引中包含city列,存储引擎直接在联合索引中按city like "%Z%"进行过滤,过滤后剩下2条记录;

  • 根据过滤后的记录的id值,逐一进行回表扫描,去聚簇索引中取出完整的行记录,并把这些记录返回给Server层;

  • Server层根据WHERE语句的其它条件age > 25,再次对行记录进行筛选,最终只留下("LiSi", "ZhengZhou", 30)这条记录。

0aa25b0395012d8e26ceeb8d113893c0.png

InnoDB存储引擎来说,索引下推只适用于二级索引(也叫辅助索引),索引下推的目的是为了减少回表次数,也就是要减少IO操作。对于InnoDB聚簇索引来说,完整的行记录已经加载到缓存区了,索引下推也就没什么意义了。

发表评论

表情:
评论列表 (有 0 条评论,101人围观)

还没有评论,来说两句吧...

相关阅读

    相关 mysql 模糊查询

    SQL模糊查询,使用like比较关键字,加上SQL里的通配符,请参考以下:  1、LIKE'Mc%' 将搜索以字母 Mc 开头的所有字符串(如 McBadden)。