mysql索引失效的坑

news/2024/11/27 19:48:36/

目录

前言

博客文章分享

对索引字段做函数操作

隐式类型转换

隐式字符编码转换

原因分析

解决方法:


前言

在MySQL中,有时候会因为语句使用不当导致sql不走索引,从而使数据库的压力变大。本篇分享一下sql不走索引的坑,让大家也避避坑!

博客文章分享

MySQL索引失效的场景

对索引字段做函数操作

DROP TABLE IF EXISTS `t_file`;
CREATE TABLE `t_file`  (`id` int(20) NOT NULL COMMENT '主键',`fileid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件ID',`filename` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件名',`sort` int(11) NOT NULL COMMENT '排序',`createtime` datetime NOT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,INDEX `fileid`(`fileid`) USING BTREE,INDEX `createtime`(`createtime`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (11111, '1', '涨了', 1, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (22222, '2', 'aaaa', 2, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (33333, '21', 'asdq', 3, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (44444, '22', 'qweqwe', 4, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (55555, '23', 'wqes', 5, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (66666, '24', 'iuoty', 6, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (77777, '25', 'orooo', 7, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (88888, '26', 'asdq2', 8, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (99999, '27', '哇哦erqwr', 9, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (101010, '12', 'drfsrfe', 10, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (111111, '26', 'jkhd', 11, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (222222, '29', 'ghdwe2', 12, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (333333, '28', '规划局tu', 13, '2023-04-19 16:02:04');
INSERT INTO `high`.`t_file` (`id`, `fileid`, `filename`, `sort`, `createtime`) VALUES (444444, '24', 'sdrt3', 14, '2023-04-19 16:02:04');

假如有这么一个文件表,需求查询统计某个月份的文件。

EXPLAIN SELECT COUNT(*) FROM `t_file` where MONTH(createtime) = 4;

 结果:

        从结果看,Extra 字段的 Using index,表示的是使用了覆盖索引。     

        首先B+ 树提供的快速定位能力,来源于同一层兄弟节点的有序性。对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。但不是说MySQL优化器不走createtime索引了,只是放弃了树搜索功能,优化器可以选择遍历主键索引或者索引 createtime,优化器对比索引大小后发现,索引 createtime更小,遍历这个索引比遍历主键索引来得更快。因此最终还是会选择索引 createtime。

        如果想使用上mysql的快速树搜索功能可以将sql改成下面这样的sql,但是这个sql的话每当有一年的数据就得加一行条件。

EXPLAIN SELECT COUNT(*) FROM `t_file` where 
(createtime>=2023-04-01 AND createtime<2023-05-01)
or (createtime>=2022-04-01 AND createtime<2022-05-01);

 建议不要在sql中对索引字段进行函数操作

隐式类型转换

还是使用t_file这个表,查询fileid等于22,索引字段是String但是输入是整形Int

EXPLAIN SELECT * FROM `t_file` where fileid = 22;

结果:

        从结果可以看出没有走索引,全表扫描了。

        规则:在 MySQL 中,字符串和数字做比较时,是将字符串转换成数字。

        所以sql语句在优化器中相当于下面这个sql

EXPLAIN SELECT * FROM `t_file` where CAST(fileid AS signed int) = 22;

 触发条件对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。

        那么如果索引字段是int类型,但是输入的参数是string呢?

        因为MySQL的规则字符串和数字做比较时,是将字符串转换成数字,所以会走索引不会全表扫描。

隐式字符编码转换

       原因分析

        首先创建一个CHARACTER SET = utf8的表

DROP TABLE IF EXISTS `file_detail`;
CREATE TABLE `file_detail`  (`id` int(11) NOT NULL COMMENT 'id',`fileid` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件ID',`fileaddress` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件路径',`user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '上传用户',PRIMARY KEY (`id`) USING BTREE,INDEX `fileid`(`fileid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `high`.`file_detail` (`id`, `fileid`, `fileaddress`, `user`) VALUES (1, '1', 'c:/conf', '张三');
INSERT INTO `high`.`file_detail` (`id`, `fileid`, `fileaddress`, `user`) VALUES (2, '21', 'c:/config', '李四');
INSERT INTO `high`.`file_detail` (`id`, `fileid`, `fileaddress`, `user`) VALUES (3, '2', 'd:/high', 'wangwu');

 然后查询一下t_file与file_detail中fileid=21的信息,sql如下:

EXPLAIN SELECT fd.* FROM t_file tf,`file_detail` fd where  tf.fileid=fd.fileid and tf.id= 33333

结果:

        从结果上看sql执行顺序是:首先使用了主键索引从t_file取出ID为33333的这一行数据,然后从这行数据中取到fileid的值,最后用fileid的值去file_detail去做匹配。

        问题:file_detail表中fileid设置了索引,但是EXPLAIN结果确没有使用到?

        百度结果是:t_file使用的utf8mb4编码,file_detail使用的utf8编码,表连接查询时用不上关键字段的索引。划重点:字符集 utf8mb4 是 utf8 的超集,所以当这两个类型的字符串在做比较的时候,MySQL 内部的操作是,先把 utf8 字符串转成 utf8mb4 字符集,再做比较。

 MySQL 内部的操作相当于如下sql:

EXPLAIN SELECT * FROM file_detail WHERE CONVERT(fileid USING utf8mb4) = 21

        所以说最后用fileid的值去file_detail去做匹配时,连接过程中对索引字段做了函数操作,导致没有用上fileid索引。

知识点: CONVERT(expr USING transcoding_name) 函数是字符串转成 utf8mb4字符集。

 解决方法:

        1、将表的字符集改变成一致的utf8mb4(生产中不建议该操作)

        ALTER TABLE `high`.`file_detail` CHARACTER SET = utf8mb4;

        2、 不能修改字符集 ,麻烦点修改sql

EXPLAIN SELECT fd.* FROM t_file tf,`file_detail` fd where  fd.fileid=CONVERT(tf.fileid USING utf8) and tf.id= 33333

总结:

 写sql时多EXPLAIN一下,不要在sql中对索引字段进行函数操作,索引字段的参数可以用函数。


http://www.ppmy.cn/news/48849.html

相关文章

SCUACM2023集训前训练-基础算法

文章目录 A-最长公共子序列转最长上升子序列模板题B-单调栈贪心&#xff0c;好题单调栈贪心神犇代码赏析 C-Atcoder_abc294_f-二分答案D-set或树状数组 A-最长公共子序列转最长上升子序列模板题 传送门 模板题我就不写了。 本文juejin&#xff1a;https://juejin.cn/post/72…

2023自助洗车店系统解决方案共享洗车无人洗车风口

2021年中国汽车保有量预计超6.3亿辆,洗车市场需求巨大,传统洗车投资大、费用贵、成本高耗水大、占地面积大,而自助洗车机占据传统洗车耗水量1/4 ,占地面积1/70 ;节能环保得到政府的大力支持,且结合信息物联技术,实现智能化管理,高效能运营,灵活便捷服务,符合智慧城市发展原则,成…

提高硬件设计能力的学习路线

不懂硬件的人&#xff0c;会觉得硬件高深莫测&#xff0c;“为什么他改几个电阻、电容就调出来&#xff0c;我弄个半天没搞定&#xff1f;”&#xff0c;“噢&#xff0c;靠的是经验”&#xff0c;但是经验又是什么呢&#xff1f;不能形容&#xff0c;反正就是不明觉厉。 就是…

36岁大龄程序员被裁,找了2个月工作,年包从100万降到50万,要不要接?

为了找到工作&#xff0c;你愿意接受降薪多少&#xff1f; 一位36岁的杭州程序员问&#xff1a; 36岁被裁&#xff0c;找了2个月工作&#xff0c;年包从100万降到50万&#xff0c;真心纠结&#xff0c;要不要接&#xff1f; 网友们分成了旗帜鲜明的两派&#xff0c;一派人认为不…

Node 07-nvm

nvm 介绍 nvm 全称 Node Version Manager 顾名思义它是用来管理 node 版本的工具&#xff0c;方便切换不同版本的Node.js 使用 nvm 的使用非常的简单&#xff0c;跟 npm 的使用方法类似 下载安装 首先先下载 nvm&#xff0c;下载地址 https://github.com/coreybutler/nvm-…

C#基础学习--LINQ

目录 什么是LINQ LINQ提供程序 匿名类型 投影初始化语句 方法语法和查询语法 查询变量 查询表达式的结构 from子句 join子句 什么是联结 查询主体中的 from...let...where片段 from 子句 let 子句 where 子句 orderby 子句 ​编辑select....group子句 查询中的匿名…

电脑提示vcruntime140_1.dll丢失的正确修复方法

vcruntime140_1.dll是vs2010编译的程序默认的库文件它的丢失易导致游戏、应用软件等程序运行出现错误&#xff0c;致使程序无法正常运行&#xff0c;下面小编教大家电脑提示vcruntime140_1.dll丢失的正确修复教程 首先打开一个360&#xff0c;搜狗等等电脑浏览器&#xff0c;输…

C#实现将文件、文件夹压缩为压缩包

C#实现将文件、文件夹压缩为压缩包 一、C#实现将文件、文件夹压缩为压缩包核心 1、介绍 Title&#xff1a;“基础工具” 项目&#xff08;压缩包帮助类&#xff09; Description步骤描述&#xff1a; 1、创建 zip 存档&#xff0c;该文档包含指定目录的文件和子目录&#xf…