【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

这样就能利用到索引了。
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
如果您在 MySQL 数据库中为字段添索引,但索引似乎没有生效,可以尝试以下几个步骤来解决问题: 1. 确认索引是否正确创建:使用 `SHOW INDEX FROM table_name` 命令来检查索引是否已正确创建。确保索引的列名、表名和索引类型都是正确的。 2. 确认索引选择是否合适:MySQL 使用不同类型的索引,如 B-Tree 索引、哈希索引等。对于不同的查询类型,选择合适的索引类型是至关重要的。确保您选择的索引类型适用于您的查询需求。 3. 确认查询是否使用索引:有时,即使索引已正确创建,但查询可能仍未使用它。您可以使用 `EXPLAIN SELECT` 命令来查看查询计划,并确认是否使用了正确的索引。如果查询计划中没有索引或使用了错误的索引,您可能需要重新编写查询或添提示(hint)来强制使用正确的索引。 4. 确认数据分布是否均匀:如果索引列中的数据分布不均匀,即某些值出现频率很高,而其他值则很少出现,那么索引可能无法发挥作用。在这种情况下,您可以考虑使用更适合数据分布的索引策略,如前缀索引、组合索引等。 5. 确认是否存在查询优化问题:有时,查询本身可能存在性能问题,即使索引已正确使用。您可以通过检查查询语句、表结构和索引设计来确定是否有优化的空间。可能需要重新编写查询、调整表结构或者添额外的索引来提高性能。 如果上述步骤都没有解决您的问题,您可能需要进一步分析数据库的配置、硬件资源以及其他因素,以找出导致索引未生效的原因。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨幂等

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值