15个必备的Mysql索引失败场景,别再陷入那个陷阱了!制作人:
|一种看待编程的新方式(ID:ershixiong_see_world)
背景
无论你是技术大佬还是职场初学者,时不时就会踏入不使用索引的Mysql数据库的陷阱。明明在场上加了索引,但没有生效,这是一个常见的现象。
几天前,我遇到了一个有点不寻常的场景。同一条 SQL 语句对某些参数有效,但对其他参数无效。为什么是这样?
还有,无论是面试还是日常生活,都需要了解和学习Mysql索引失败的常见情况。
为了方便学习和记忆,本文档总结了15种常见的不按照索引的情况,并给出了例子,以便大家更好的避免误入陷阱。建议保留以备不时之需。数据库和索引准备 id:数据库主键;
union_idx
:这是由id_no、username和age组成的联合索引; create_time_idx
:这是通常由create_time组成的索引; 初始化数据
初始化数据分为两部分:基础数据和批量导入数据。
基础数据中插入4条数据,其中第4条数据生成时间为未来,用于后续特殊场景的验证:
INSERT INTO `t_user` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1001', 'Tom1', 11, '2022-02-27 09:04:23');
INSERT INTO `t_user` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1002', 'Tom2', 12, '2022-02-26 09:04:23');
INSERT INTO `t_user` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1003', 'Tom3', 13, '2022-02-25 09:04:23');
INSERT INTO `t_user` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1004', 'Tom4', 14, '2023-02-25 09:04:23');
除了基础数据外,还存储了存储过程及其调用的SQL,数据方便批量插入,用于查看数据较多的场景:
-- 删除历史存储过程
DROP PROCEDURE IF EXISTS `insert_t_user`
-- 创建存储过程
delimiter $
CREATE PROCEDURE insert_t_user(IN limit_num int)
BEGIN
DECLARE i INT DEFAULT 10;
DECLARE id_no varchar(18) ;
DECLARE username varchar(32) ;
DECLARE age TINYINT DEFAULT 1;
WHILE i < limit_num DO
SET id_no = CONCAT("NO", i);
SET username = CONCAT("Tom",i);
SET age = FLOOR(10 + RAND()*2);
INSERT INTO `t_user` VALUES (NULL, id_no, username, age, NOW());
SET i = i + 1;
END WHILE;
END $
-- 调用存储过程
call insert_t_user(100);
关于存储过程的创建和存储,暂时无法执行,需要时才执行。
数据库版本及执行计划
查看当前数据库版本:
select version();
8.0.18
以上是我测试的数据库版本: 查看SQL语句的执行计划,我们通常使用关键字 执行示例: 执行结果: 可以看到上面的SQL语句使用了主键索引(PRIMARY), 准备好以上数据和知识。让我们首先解释索引失败的具体示例。 合并索引遵循最左对齐原则。顾名思义,联合索引中最左边的字段首先匹配字符。因此,在创建联合索引时,会将最常用的字段移至where子句中联合索引的左边缘。 查询时如果希望查询条件被索引,需要满足以下要求:最左边的字段必须出现在查询条件中。在示例 中,联合索引 最左边的字段是 id_no。一般情况下,只要查询条件中出现id_no,就使用联合索引。 ? 这是索引。 这里是key_len的计算: 上面展示了一种情况下key_len的计算过程。将来,您无需单独下载它们。了解基本组成和原理就足够了。您可以自己控制更多情况。示例2 索引,根据上面key_len的分析,粗体提示,在使用索引时,我们不仅使用 当然也有三列都包含在查询条件中的情况,就不举例了。以上都是前瞻性的索引示例,即符合最左匹配原则 反转示例: 解释结果: 目前可以看到没有使用索引,也就是说索引无效。 同样,只要不出现下面最左边的条件组合,索引也会失效: 那么第一种索引失败场景: 普通索引场景中,查询条件不匹配最左边的匹配原则。 《阿里巴巴开发手册》 的 ORM 映射 在[强制]表]中必须指定:在查询的情况下,不要使用 * 作为列表必须明确指出查询字段的数量,哪些字段必须填写。说明:1)增加查询分析器的分析成本。 2)增减字段很容易与resultMap配置不兼容。 3)无用字段会增加网络消耗,尤其是文本字段。 虽然规范手册中没有讨论索引问题,但使用 比如上面组合索引中的查询条件是年龄或者用户名,那么使用 但是如果你想根据username(所有索引字段)查询id_no、username、age这三个结果,并明确查询结果字段,可以使用 无论查询条件是否为 ,都会使用索引。根据key_len可以看到索引的所有列都在使用中。 第二种索引失败场景:在合并索引下,尝试使用干净的查询列来替代使用覆盖索引; 这种情况,当你不使用索引时,是一个优化元素。如果满足业务If场景,则继续请求对SQL语句进行索引。至于阿里巴巴开发手册中的规范,只是两者有冲突。规范本身并未为此索引规则定义。 ?转到索引。 这样的话,不仅是索引问题,还会增加数据库的计算负载。以上面的SQL语句为例,数据库必须扫描整个表找出所有id字段的值,然后计算它们,然后与参数值进行比较。如果所有执行都经过上述步骤,性能下降是可想而知的。 推荐的使用方式是先计算内存中的期望值或者计算SQL语句条件右侧的参数值。 上例优化如下: 第三种索引错误情况:索引列参与导致全表扫描,索引错误。 ? 。 目前索引失败的原因与第三种情况相同。这是因为数据库要先进行全表扫描,获取到数据之后再进行接受和计算,从而导致索引失效。然而,也存在性能问题。 示例仅列出了 SUBSTR 函数。类似的情况也发生在类似的函数如 CONCAT 上。解决方法可以参考第三种情况。您可以考虑使用内存计算或其他内容处理方法来减少数据库。 第四种索引错误情况:索引列参与函数处理,导致全表扫描,索引错误。 解释结果: i真的很喜欢 ❙频繁但不正确的使用通常会导致无索引。常见的使用方法有: 1。方法和方法3一样,由于占位符出现在头部,所以不能使用索引。在这种情况下缺乏索引的原因很容易理解。索引本身对应一个目录,从左到右一一排列。占位符位于条件的左侧,这使得无法通过正常目录进行匹配,这对于索引失败来说是正常的。 第五个索引错误:在模糊查询(如指令)过程中,模糊匹配占位符位于条件的头部。 ?扫描。 索引错误的原因:varchar和int是两种不同的类型。解决 的方法是用单引号或双引号将参数 第六种索引错误情况:参数的类型与字段的类型不匹配,导致类型隐式转换,索引失败。 这种情况有一个特殊情况。如果字段类型为int类型,并且查询条件添加了单引号或双引号,Mysql会将参数转换为int类型,即使它们使用单引号或双引号: 上述语句仍然会被索引。 OR 日常生活中最常用的运算关键字,但使用不当也会导致索引失败。 ? ,索引确实失败了。 而且,如果我们换个角度想,如果只用 但是如果or两边都有“">”和“8.0.18
。当然,以下所有示例也可以在其他版本中进行检查。
explain
根据执行结果判断索引使用情况。explain select * from t_user where id = 1;
解释
❀的值为4; 之间
key_len的含义
:表示索引使用的字节数。这个值可以用来判断索引的使用情况。尤其是在组合索引时,判断索引的使用量非常重要。 1 合并索引不符合最左对齐原则
union_idx
由以下内容组成: KEY `union_idx` (`id_no`,`username`,`age`)
id_no
类型是varchar(18),字符集是utf8mb4_bin,这意味着我们用4个字节来表示完整的UTF-8。目前 key_len = 18* 4 = 72; id_no
列,还使用username
列。示例 3 与第一个示例类似,仅使用 Arrivedid_no
列。 的示例。我们来看看不遵循这个原则的逆向例子。
explain select * from t_user where username = 'Tom2' and age = 12;
explain-04
explain select * from t_user where age = 12;
explain select * from t_user where username = 'Tom2';
2 使用 select *
select *
语句可以有一个附带的好处:在某些情况下co 。 select *
时,肯定不会使用该索引。覆盖索引: 解释结果:
覆盖索引
用户名
explain select * from t_user where id not in (2,3);
-- 内存计算,得知要查询的id为1
explain select * from t_user where id = 1 ;
-- 参数侧计算
explain select * from t_user where id = 2 - 1 ;
5不正确使用类似的示例:
explain select * from t_user where id_no like '%00%';
indexlike
1002
括起来。 explain select * from t_user where id = '2';
7。使用OR运算
用户名字段
作为条件的话,显然是一个全表扫描的问题。由于进行了全表扫描,之前的条件id
如果再去一遍索引就太可惜了。因此,在使用or关键字时,记得给两个条件都添加索引,否则索引会失效。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。