【MySQL】明明加了索引,为何不生效?
加了索引却不生效可能会有以下几种原因。
1. 索引列是表示式的一部分,或是函数的一部分
如下SQL:
SELECT book_id FROM BOOK WHERE book_id +1 = 5;
或者:
SELECT book_id FROM BOOK WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(gmt_create) <= 10
上述两个 SQL 虽然在列 book_id 和 gmt_create 设置了索引 ,但由于它们是表达式或函数的一部分,导致索引无法生效,最终导致全表扫描。
2. 隐式类型转换
以上两种情况相信不少人都知道索引不能生效,但下面这种隐式类型转换估计会让不少人栽跟头,来看下下面这个例子:
假设有以下表:
CREATE TABLE `tradelog` (`id` int(11) NOT NULL,`tradeid` varchar(32) DEFAULT NULL,`operator` int(11) DEFAULT NULL,`t_modified` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `tradeid` (`tradeid`),KEY `t_modified` (`t_modified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
执行SQL语句
SELECT * FROM tradelog WHERE tradeid=110717;
交易编号 tradeid 上有索引,但用 EXPLAIN 执行却发现使用了全表扫描,为啥呢,tradeId 的类型是 varchar(32), 而此 SQL 用 tradeid 一个数字类型进行比较,发生了隐形转换,会隐式地将字符串转成整型,如下:
mysql> SELECT * FROM tradelog WHERE CAST(tradid AS signed int) = 110717;
这样也就触发了上文中第一条的规则 ,即:索引列不能是函数的一部分。
3. 隐式编码转化
这种情况非常隐蔽,来看下这个例子
CREATE TABLE `trade_detail` ( `id` int(11) NOT NULL, `tradeid` varchar(32) DEFAULT NULL, `trade_step` int(11) DEFAULT NULL, /*操作步骤*/ `step_info` varchar(32) DEFAULT NULL, /*步骤信息*/ PRIMARY KEY (`id`), KEY `tradeid` (`tradeid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
trade_defail 是交易详情, tradelog 是操作此交易详情的记录,现在要查询 id=2 的交易的所有操作步骤信息,则我们会采用如下方式
SELECT d.* FROM tradelog l, trade_detail d WHERE d.tradeid=l.tradeid AND l.id=2;
由于 tradelog 与 trade_detail 这两个表的字符集不同,且 tradelog 的字符集是 utf8mb4,而 trade_detail 字符集是 utf8, utf8mb4 是 utf8 的超集,所以会自动将 utf8 转成 utf8mb4。即上述语句会发生如下转换:
SELECT d.* FROM tradelog l, trade_detail d WHERE (CONVERT(d.traideid USING utf8mb4)))=l.tradeid AND l.id=2;
自然也就触发了 「索引列不能是函数的一部分」这条规则。怎么解决呢,第一种方案当然是把两个表的字符集改成一样,如果业务量比较大,生产上不方便改的话,还有一种方案是把 utf8mb4 转成 utf8,如下
mysql> SELECT d.* FROM tradelog l , trade_detail d WHERE d.tradeid=CONVERT(l.tradeid USING utf8) AND l.id=2;
这样索引列就生效了。
4. 使用 order by 造成的全表扫描
SELECT * FROM user ORDER BY age DESC
上述语句在 age 上加了索引,但依然造成了全表扫描,这是因为我们使用了 SELECT *,导致回表查询,MySQL 认为回表的代价比全表扫描更大,所以不选择使用索引,如果想使用到 age 的索引,我们可以用覆盖索引来代替:
SELECT age FROM user ORDER BY age DESC
或者加上 limit 的条件(数据比较小)
SELECT * FROM user ORDER BY age DESC limit 10
这样就能利用到索引了。
【MySQL】明明加了索引,为何不生效?相关推荐
- mysql关于or的索引问题_SQL优化 MySQL版 - 避免索引失效原则(二)
作者 : Stanley 罗昊 体验SQL优化中的概率情况 在上一篇文章结尾处,我们在执行查询计划的时候,却发现我明明加了索引,并且也满足了使用索引的条件,但是,给我的优化结果却是失败,从而,得出一个 ...
- MySQL中B+树索引,聚簇索引,二级索引,辅助索引,回表,索引生效条件
对于MySQL,我们经常说调优有一个手段就是加索引,那么为什么加索引能够优化查询,是不是加了索引查询就快了 ? 在MySQL中,存储的单元并不是按照我们理解的一条一条记录,而是按照页来进行存储的,My ...
- 明明加了唯一索引,为什么还是产生重复数据?
前言 前段时间我踩过一个坑:在mysql8的一张innodb引擎的表中,加了唯一索引,但最后发现数据竟然还是重复了. 到底怎么回事呢? 本文通过一次踩坑经历,聊聊唯一索引,一些有意思的知识点. 1.还 ...
- mysql 添加索引慢_我就加个索引而已,怎么服务就挂了???
❝ 领导让我SQL优化,我直接把服务干挂了... ❞ 前言 MySQL大表加字段或者加索引,是有一定风险的. 大公司一般有DBA,会帮助开发解决这个痛点,可是DBA是怎么做的呢? 小公司没有DBA,作 ...
- tp5更新某字段加1_爱可生详解MySQL 8.0:索引特性1-函数索引
函数索引顾名思义就是加给字段加了函数的索引,这里的函数也可以是表达式.所以也叫表达式索引. MySQL 5.7 推出了虚拟列的功能,MySQL8.0的函数索引内部其实也是依据虚拟列来实现的. 我们考虑 ...
- mysql 批量加索引_mysql优化:按期删数据 + 批量insert + 字符串加索引为何很傻
嗯,犯了一个很低级的错误,最近暴露出来了.html 背景:mysql 1. 内部平台,接口间断性无返回,查询日志注意到失败时,接口耗时达到4000+(正常状态:100+ms)git 2. 增长日志打点 ...
- mysql datetime month不走索引_like百分号加前面一定不走索引吗?一不小心就翻车,关于mysql索引那些容易错的点...
like百分号加前面一定不走索引吗? 正常来讲,我们都知道在mysql的like查询中,百分号加在关键词后面是走索引的,比如 select * like "张三%",而百分号在前面 ...
- MySQL锁机制,行锁jingran加在索引上
锁概述 锁是计算机协调多个进程或线程并发访问某一资源的机制,应该都不陌生.?但在这之前我们先来看看并发控制,理清MVCC多版本并发控制和锁的关系,这也是之前我很迷惑的一个点 并发控制技术 在数据库中, ...
- mysql or走索引吗_加了索引,mysql查询就一定会用吗?
小白白跑去鹅厂面试,面试官提出了一个很实际的问题: mysql增加索引,那些情况会失效呢?谈一下实际工作中遇到的情况.我们的小白白又抛出了白氏秘籍:用不用索引,找DBA小姐姐!啊?这是你面试哈,还是D ...
最新文章
- wordpress php教程 pdf,wordpress二次开发全能教程.pdf
- FFmpeg中拉取rtsp视频流并缩放显示测试代码
- 认清Hadoop和Spark的这几点区别,学习时才能事半功倍
- Linux kernel 不输出log信息
- 【转】PHP foreach 小结
- 【jquery模仿net控件】简单的datalist控件更新,及其简单应用
- java大公司后端多线程面试题最强分享
- 绘制对象iPhone开发基础教程 笔记
- Python基础教程:括号()[]{}详解
- Linux技巧:多核下绑定硬件/进程到不同CPU
- Delphi 的运算符重载(1)
- RocketMQ之事务消息
- 详解-OTUS(大津法-最大类间方差)原理及C语言代码实现
- Android 要收费、闭源恐难于上青天
- C语言学习笔记---指针
- 不小心合并了icloud通讯录_苹果手机号码被删除如何恢复?找回通讯录的具体步骤...
- 关于“undefined reference to”错误
- macOS应用程序打开时出现崩溃的情况,怎样处理?
- sendTemplateMessage微信小程序消息推送 前段 + 后端(thinkphp3.2)
- java php 通讯录,基于ssh/bs/java/asp.net/php/web通讯录管理系统