您现在的位置是:亿华云 > 系统运维
后端程序员必备:索引失效的十大杂症
亿华云2025-10-04 01:00:50【系统运维】0人已围观
简介背景最近生产爆出一条慢sql,原因是用了or和!=,导致索引失效。于是,总结了索引失效的十大杂症,希望对大家有帮助,加油。一、查询条件包含or,可能导致索引失效新建一个user表,它有一个普通索引us
背景
最近生产爆出一条慢sql,后端原因是程序用了or和!=,导致索引失效。员必引失于是备索,总结了索引失效的大杂十大杂症,希望对大家有帮助,后端加油。程序
一、员必引失查询条件包含or,备索可能导致索引失效
新建一个user表,大杂它有一个普通索引userId,后端结构如下:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT,程序 `userId` int(11) NOT NULL, `age` int(11) NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
分析&结论:
对于or+没有索引的age这种情况,假设它走了userId的员必引失索引,但是备索走到age查询条件时,它还得全表扫描,大杂也就是需要三步过程:全表扫描+索引扫描+合并 如果它一开始就走全表扫描,直接一遍扫描就完事。 mysql是有优化器的,处于效率与成本考虑,遇到or条件,让索引失效,看起来也合情合理嘛。注意: 如果or条件的列都加了索引,索引可能会走的云南idc服务商,大家可以自己试一试。
二、如何字段类型是字符串,where时一定用引号括起来,否则索引失效
假设demo表结构如下:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;userId为字符串类型,是B+树的普通索引,如果查询条件传了一个数字过去,它是不走索引的,如图所示:
如果给数字加上,也就是传一个字符串呢,当然是走索引,如下图:
分析与结论:
为什么第一条语句未加单引号就不走索引了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。
三、like通配符可能导致索引失效。
并不是用了like通配符,索引一定失效,而是云服务器提供商like查询是以%开头,才会导致索引失效。
表结构:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;like查询以%开头,索引失效,如图:
把%放后面,发现索引还是正常走的,如下:
把%加回来,改为只查索引的字段(覆盖索引),发现还是走索引,惊不惊喜,意不意外
结论:
like查询以%开头,会导致索引失效。可以有两种方式优化:
使用覆盖索引 把%放后面附: 索引包含所有满足查询需要的数据的索引,称为覆盖索引(Covering Index)。
四、联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
表结构:(有一个联合索引 idx_userid_age, userId在前, age在后)
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` int(11) NOT NULL, `age` int(11) DEFAULT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userid_age` (`userId`,`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;在联合索引中,查询条件满足最左匹配原则时,索引是云服务器正常生效的。请看demo:
如果条件列不是联合索引中的第一个列,索引失效,如下:
分析与结论:
当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。 联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的。五、在索引列上使用mysql的内置函数,索引失效。
表结构:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `loginTime` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) USING BTREE, KEY `idx_login_time` (`loginTime`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;虽然loginTime加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图:
六、对索引列运算(如,+、-、*、/),索引失效。
表结构:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;虽然age加了索引,但是因为它进行运算,索引直接迷路了。。。山重水复疑无路,算着算着脑瓜疼,索引就真的不认识路了。如图:
七、索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
表结构:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` int(11) NOT NULL, `age` int(11) DEFAULT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;虽然age加了索引,但是使用了!= 或者 < >,not in这些时,索引如同虚设。如下:
八、索引字段上使用is null, is not null,可能导致索引失效。
表结构:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `card` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) USING BTREE, KEY `idx_card` (`card`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;单个name字段加上索引,并查询name为非空的语句,其实会走索引的,如下:
单个card字段加上索引,并查询name为非空的语句,其实也会走索引的,如下:
但是它们用or连接起来,索引就失效了,如下:
九、左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
新建两个表,一个user,一个user_job
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; CREATE TABLE `user_job` ( `id` int(11) NOT NULL, `userId` int(11) NOT NULL, `job` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;user 表的name字段编码是utf8mb4,而user_job表的name字段编码为utf8。
执行左外连接查询,user_job表还是走全表扫描,如下:
如果把它们改为name字段编码一致,还是会一路高歌,雄赳赳,气昂昂,走向索引。
十、mysql估计使用全表扫描要比使用索引快,则不使用索引。
当表的索引被查询,会使用最好的索引,除非优化器使用全表扫描更有效。优化器优化成全表扫描取决与使用最好索引查出来的数据是否超过表的30%的数据。 不要给性别等增加索引。如果某个数据列里包含了均是"0/1"或“Y/N”等值,即包含着许多重复的值,就算为它建立了索引,索引效果不会太好,还可能导致全表扫描。Mysql出于效率与成本考虑,估算全表扫描与使用索引,哪个执行快。这跟它的优化器有关,来看一下它的逻辑架构图吧(图片来源网上)
总结
总结了索引失效的十大杂症,在这里来个首尾呼应吧,分析一下我们生产的那条慢sql。模拟的表结构与肇事sql如下:
CREATE TABLE `user_session` ( `user_id` varchar(32) CHARACTER SET utf8mb4 NOT NULL, `device_id` varchar(64) NOT NULL, `status` varchar(2) NOT NULL, `create_time` datetime NOT NULL, `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`user_id`,`device_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; explain update user_session set status =1 where (`user_id` = 1 and `device_id`!=2) or (`user_id` != 1 and `device_id`=2)分析:
执行的sql,使用了 or条件,因为组合主键( user_id, device_id),看起来像是每一列都加了索引,索引会生效。 但是出现 !=,可能导致索引失效。也就是 or+ !=两大综合症,导致了慢更新sql。解决方案:
那么,怎么解决呢?我们是把 or条件拆掉,分成两条执行。同时给 device_id加一个普通索引。
最后,总结了索引失效的十大杂症,希望大家在工作学习中,参考这十大杂症,多点结合执行计划 expain和场景,具体分析,而不是按部就班,墨守成规,认定哪个情景一定索引失效等等。
很赞哦!(3819)
相关文章
- CNAME:对应解析的记录值为域名地址
- 前端程序员:巧用CSS圆角实现有点意思的加载动画
- 聊聊ReentrantLock 中的四个坑!
- HarmonyOS服务卡片开发知识总结
- 域名不仅仅是一个简单的网站。对于有长远眼光的公司来说,在运营网站之前确定一个优秀的域名对有长远眼光的公司来说是非常重要的。这对今后的市场营销、产品营销和企业品牌建设都具有十分重要的意义。优秀的域名是企业在市场竞争中获得持久优势的利器。
- 编程语言排行:Java和Python并列第二、Dart首次进入前20
- CSS 即将支持嵌套,SASS/LESS 等预处理器已无用武之地?
- 为什么ThreadLocal容易导致内存泄漏?
- 最后提醒我们,域名到期后要及时更新域名,否则可能会丢掉域名,每次抢先注册都不会成功。
- 基于React/Vue搭建一个通用的表单管理配置平台
站长推荐
当投资者经过第二阶段的认真学习之后又充满了信心,认为自己可以在市场上叱咤风云地大干一场了。但没想到“看花容易绣花难”,由于对理论知识不会灵活运用.从而失去灵活应变的本能,就经常会出现小赢大亏的局面,结果往往仍以失败告终。这使投资者很是困惑和痛苦,不知该如何办,甚至开始怀疑这个市场是不是不适合自己。在这种情况下,有的人选择了放弃,但有的意志坚定者则决定做最后的尝试。
手把手教你用Pandas分析全国城市房价
Unity宣布将以3.2亿美元收购Parsec远程桌面工具
组织的应用程序架构是如何演变的?
众所周知,com域名拥有最大的流通市场和流通历史。最好选择com域名,特别是在购买域名时处理域名。其次可以是cn域名、net域名、org域名等主流域名,现在比较流行的王域名和顶级域名,都是值得注册和投资的。
保证Kubernetes生产环境安全七条建议
Spring Cloud 开发内存占用过高,咋解决?
这几天,写了一个Strview.js