mysql联表时联表条件的索引失效

在MySQL中,联表查询时发现索引失效,问题出在不同表的相同字段字符集排序规则不一致。经过调整,将两个字段的排序规则改为相同后,索引成功启用,查询性能显著提升。该问题揭示了字符集排序规则在数据库优化中的重要性,对于遇到类似问题的开发者具有参考价值。

mysql联表时,联表条件的索引失效

序:因为在项目开发中,有个查询功能非常的慢,连了五六张表,然后我就用explain分析了一下,发现两张大表连接时竟然没用上索引。

a left join b on a.ccc = b.yyyy
ccc和yyyy都分别建有索引

baidu一下,大家都说的差不多(一些什么情况可能会导致索引失效,以后整理一下,这里就不说了),我就问同事,为什么这两个字段都建立了索引就是用不上呢,同事突然灵光一动“是不是字符编码不一样”。
我赶紧去两个表查看这两个字段的字符编码,“嗯,是一样的。”

——但是!排序规则不一样,如下图:
a表
b表
我用这两个字段相等进行连表:a.ccc = b.yyyy(假设)
可以看到,这两个字段的字符集是一样的,都是utf8mb4
而排序规则不一样,a表的ccc的排序规则是utfmb4_general_ci,而b表的yyyy的排序规则是utfmb4_bin,因为字符集不同会导致索引失效,所以我合理怀疑排序规则不同也会导致索引失效。

然后,我就让我们开发经理将两个字段的排序规则修改一致,再次explain,果然索引用上了!!!(瞬间从20多秒减少到0点几秒,当然还需继续优化)

——在此记录一下,如果也有别的小伙伴遇到了这样的问题,可以参考一下哦~

附:
各种排序规则的大致区别:
(utf8_bin,utf8_unicode_ci、utf8_general_cs、utf8_general_ci等等)
以_bin结尾的表示用二进制数据编译存储,是区分大小写的
以_ci结尾的表示不区分大小写(ci是case insensetive)
以_cs结尾的表示区分大小写(cs是case sensetive)

utf8_unicode_ci:通常用unicode对中文字符做处理,防止出现乱码,在英文下没有区别

一般来说,设置了字符集,也就设置了默认的排序规则,比如:
字符集:utf8
排序规则默认就是:utf8_general_ci

更多深入了解就需要自己去找Baidu了。

<think>嗯,用户想了解MySQL索引失效的常见场景及原因分析。我记得之前看过一些资料,比如当查询条件里用了函数或者运算,索引可能会失效。比如,如果有一个字段是日期类型,但查询候用了DATE_FORMAT函数转换格式,这样索引就用不上了。还有,当使用LIKE查询候,如果通配符%放在前面,比如'%abc',这候索引也会失效,因为最左匹配原则不满足。 然后,还有类型转换的问题。比如字段是字符串类型,但查询候用了数字,这样MySQL会隐式转换类型,导致索引失效。比如user_id是varchar类型,但查询候写成WHERE user_id = 123,这候可能不会走索引。 合索引的话,如果查询条件没有按照最左前缀的顺序,比如索引是(a, b, c),但查询只用了b和c,这候索引可能用不上。或者中间跳过了某个字段,比如用了a和c,但没有b,这候只能用到a的部分,后面的字段无法使用索引。 还有OR操作符,如果OR两边的条件中有字段没有索引,那么整个查询可能会全扫描。比如WHERE a=1 OR b=2,如果b没有索引,那么即使a有索引,整个查询也不会使用索引。 另外,索引列参与计算的话,比如WHERE age + 1 > 20,这候索引也会失效,因为需要计算每一行的age值,无法直接使用索引树。 还有数据分布的问题,如果某个值的数据量特别大,比如状态字段有90%都是1,这查询status=1可能不会走索引,因为全扫描更快。优化器会根据成本来选择执行计划。 覆盖索引的情况,如果查询的列都在索引中,就可以避免回,提升效率。但如果需要回,而数据量很大,优化器可能认为全扫描更优,导致索引失效。 隐式字符集转换也是一个问题,比如查询,两个的字符集不一致,可能会导致索引失效。这候需要统一字符集。 另外,使用NOT IN或者<>的候,如果范围太大,优化器可能选择全扫描。还有索引统计信息不准确的话,可能需要ANALYZE TABLE来更新统计信息。 索引设计方面,应该优先使用唯一索引,为常用查询字段建索引,避免冗余索引。还有查询避免对索引列进行运算,保持最左前缀原则,合理设计合索引的顺序。 可能需要检查用户的问题是否覆盖了这些场景,或者有没有遗漏的情况。比如,有没有提到IS NULL或者IS NOT NULL的情况?根据引用中的内容,MySQL决定是否使用索引是基于成本,而不是这些操作符本身,所以即使用了IS NULL,如果成本低,还是会用索引。这点需要澄清,避免误解。 总结起来,常见的失效场景包括:不符合最左前缀原则、类型转换、对索引列进行计算或函数操作、OR条件中的非索引字段、LIKE以%开头、数据分布不均匀导致全扫描更优、合索引中间断点、隐式字符集转换、统计信息不准确、以及使用覆盖索引的考虑。 需要将这些点整理出来,结合引用中的资料,给出清晰的解释和例子,帮助用户理解每个场景的原因和解决方法。</think>### MySQL索引失效的常见场景及原因分析 #### 一、索引失效的核心原因 MySQL是否使用索引取决于**成本估算**,而非单纯语法规则。优化器会基于数据分布、统计信息等因素选择成本最低的执行计划[^4]。 --- #### 二、常见索引失效场景及示例 1. **违反最左前缀原则** - **场景**:合索引$(a,b,c)$,但查询条件未以$a$开头 - **示例**: ```sql SELECT * FROM table WHERE b=2 AND c=3; -- 无法使用索引 SELECT * FROM table WHERE a=1 AND c=3; -- 仅使用到a字段索引 ``` 2. **对索引列进行计算或函数操作** - **原因**:索引存储原始值,计算后无法直接匹配 - **示例**: ```sql SELECT * FROM table WHERE YEAR(create_time) = 2023; -- 索引失效 SELECT * FROM table WHERE create_time >= '2023-01-01'; -- 有效使用索引 ``` 3. **隐式类型转换** - **场景**:索引字段为字符串类型,但查询使用数值类型 - **示例**: ```sql SELECT * FROM table WHERE phone = 13800138000; -- phone为varchar类型,索引失效 ``` 4. **LIKE以通配符%开头** - **原因**:破坏索引的**有序性**,无法利用B+树结构快速定位[^2] - **示例**: ```sql SELECT * FROM table WHERE name LIKE '%张三'; -- 全扫描 SELECT * FROM table WHERE name LIKE '张%'; -- 可使用索引 ``` 5. **OR连接非索引字段** - **场景**:OR条件中存在未索引字段触发全扫描 - **示例**: ```sql SELECT * FROM table WHERE a=1 OR d=2; -- 若d字段无索引,索引失效 ``` 6. **数据分布不均匀** - **原因**:当查询条件命中大量数据,优化器认为全扫描更快[^4] - **示例**: ```sql SELECT * FROM table WHERE status=1; -- 若status=1占90%数据,可能不使用索引 ``` 7. **索引统计信息过** - **解决方案**:定期执行`ANALYZE TABLE`更新统计信息[^3] --- #### 三、优化建议 1. **索引设计** - 优先使用唯一索引,控制单索引数量(建议不超过5个)[^5] - 合索引字段顺序按**区分度从高到低**排列 - 避免为低区分度字段(如性别)单独建索引 2. **查询优化** - 避免对索引字段进行运算或函数处理 - 使用覆盖索引减少回操作 - 对范围查询条件放在合索引最后位置 3. **系统维护** ```sql EXPLAIN SELECT ...; -- 分析执行计划 ANALYZE TABLE table_name; -- 更新统计信息 ``` --- #### 四、特殊场景补充 - **IS NULL/IS NOT NULL**:实际可能使用索引,取决于数据分布和成本估算[^4] - **字符集不一致**:查询字段字符集不同会导致隐式转换 - **范围查询中断后续索引**: ```sql WHERE a>1 AND b=2 -- 合索引(a,b)中,b字段无法使用索引 ``` --- 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值