MySQL 表字符集不同导致索引失效问题

55 篇文章 4 订阅

开发反馈sql表关联字段无法使用索引,加上hint之后还是不走,想知道原因。记录一下排查思路和遇到的问题。

一、 排查过程

explain
select t1.*
from t6
    inner join t2  on t6.id = t2.product_id
    inner join t1 FORCE INDEX(notice_desk_batch_code_ndp_id) on t1.ndp_id  = t2.id
    left join t3 on t2.od_id = t3.id
    left join t4 on t3.createdby = t4.id
    left join t5 on t5.line_id = t1.erp_line_id
where
  t5.id is null
  and t2.product_form = 1
  and t2.care_auto_insure = 0
  and t6.category_id in (1784, 2308, 2491, 2489, 2492, 2490, 2510)
  and t2.deletedby = 0;

首先检查了下 t1.ndp_id 和 t2.id 字段确实有索引,选择率也很高。

show index from 表名;

看到这个where条件猜了下会不会是返回数据量太大,检查t1表有9万行数据,查询返回结果只有60行,可以排除。

检查t1.ndp_id 和 t2.id 字段数据类型,发现都是varchar,不存在隐式转换问题。

检查t1和t2表字符集,如果字符集不同,转字符集要在字段上用cast函数,索引也会失效。

show create table 表名;

t1表

t2表

发现字符集确实是不一样的,其实认真看前面的执行计划,你会发现t2的ref部分是func。

根据官方文档的解释,func意思是该值是一个函数的返回结果。如果你没有显式用函数,很可能是MySQL有隐式的调用。5.7 可以通过show warnings查看是什么函数(5.6试了下不行)。

If the value is func, the value used is the result of some function. To see which function, use SHOW WARNINGS following EXPLAIN to see the extended EXPLAIN output. The function might actually be an operator such as an arithmetic operator.

看到这里,顺便检查了下其他表的字符集,结果如下:

可以看到t2和t6,t3和t4字符集也不相同,但为什么它们能走索引?因为关联字段是int类型的,与字符集无关。

二、 解决方法

既然知道了是字符集的问题,解决方法就是找时间重建表,调整字符集。由于业务新表均使用utf8mb4字符集,我们需要把t1表从utf8转为utf8mb4,对于5.6版本,这里还有个问题,5.6单个索引最长只支持到767 bytes。t1.ndp_id字段类型是varchar(255),对于utf8,每个字符占3 bytes,没有超过最大限制,但是utf8mb4每个字符占4 bytes,4*255明显就超了767 bytes,因此转换会遇到以下报错。

这个报错跟 innodb_large_prefix 参数有关,设置为off时(5.6默认),索引最大长度为767 bytes;设置为on时(5.7默认),索引最大长度为3072 bytes。另外该参数在新版本已经是个过时的参数,未来有可能移除。

When this option is enabled, index key prefixes longer than 767 bytes (up to 3072 bytes) are allowed for InnoDB tables that use DYNAMIC or COMPRESSED row format.

所以解决方法有两个:

  • 如果可以,改短ndp_id字段后再重建表修改字符集,导入数据
  • 修改innodb_large_prefix参数后再重建表修改字符集,导入数据

修改方法:在/etc/my.cnf设置innodb_large_prefix=on(重启生效),如果不想重启,可以在命令行设置 set  global innodb_large_prefix=on;(重启失效),两者结合保证停机时间最短。

参考

MySQL :: MySQL 8.0 Reference Manual :: 8.8.2 EXPLAIN Output Format

MySQL :: MySQL 5.7 Reference Manual :: 8.8.3 Extended EXPLAIN Output Format

MySQL :: MySQL 5.7 Reference Manual :: 14.15 InnoDB Startup Options and System Variables

关于InnoDB索引长度限制的问题 - python-yzw - OSCHINA - 中文开源技术交流社区

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
MySQL中的字符串索引可能会失效的原因有以下几个: 1. 长度过长:如果索引字段的长度超过了MySQL的限制,例如在InnoDB引擎中,utf8字符集最多只能使用3个字节示一个字符,超过这个限制的字段将无法进行索引。 2. 选择性不高:索引的选择性是指索引列中不重复的值的比例。如果索引列的选择性非常低,即大部分记录都具有相同的值,那么MySQL可能会选择不使用索引。例如,一个性别字段只有两个取值:男和女,那么对该字段进行索引就没有太大意义。 3. 数据类型不匹配:索引字段和查询条件字段类型不匹配也可能导致索引失效。例如,如果索引字段是字符串类型,而查询条件中使用了数字类型,则索引无法被利用。 4. 使用函数或达式:如果在查询条件中使用了函数或达式,MySQL无法直接使用索引进行匹配。例如,WHERE SUBSTRING(name, 1, 3) = 'abc' 的查询条件中使用了SUBSTRING函数,此时MySQL无法使用索引来加速查询。 5. 数据量过大:当中的数据量很大时,即使存在合适的索引,MySQL也可能因为优化器的选择而决定不使用索引。优化器根据查询的成本估算来决定是否使用索引,有时全扫描反而更快。 为了解决索引失效问题,可以考虑以下几点: 1. 优化索引:根据实际需求和查询条件,选择合适的索引列,保证索引的选择性足够高。 2. 避免使用函数或达式:尽量在查询条件中避免使用函数或达式,尽量保持查询条件和索引字段类型的匹配。 3. 注意索引字段的长度:根据MySQL的限制,合理设置索引字段的长度,避免超过限制。 4. 避免全扫描:尽量通过优化查询语句、添加合适的索引、分等方式来避免全扫描,提高查询性能。 总之,索引的正确使用和优化是提高MySQL查询性能的关键。根据具体情况进行调整和优化,可以显著提升查询效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hehuyi_In

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

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

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

打赏作者

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

抵扣说明:

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

余额充值