本文 首发于 🍀 永浩转载 请注明 来源

mysql 连表操作后字符集不同导致索引失效

背景

一个表的字符集为utf8mb4 一个表的字符集为utf8

连表查询后可以发现索引失效的情况,查询很慢。

t1为utf8,t2为utf8mb4。但是为什么表字符集不一样(实际是字段字符集不一样)就会导致t1全表扫描呢?

分析原因

(1)首先t2 left join t1决定了t2是驱动表,这一步相当于执行了

select * from t2 where t2.name = 'dddd'

取出code字段的值,假如为’8a77a32a7e0825f7c8634226105c42e5’;

(2)然后拿t2查到的code的值根据join条件去t1里面查找,这一步就相当于执行了

select * from t1 where t1.code = '8a77a32a7e0825f7c8634226105c42e5';

(3)但是由于第(1)步里面t2表取出的code字段是utf8mb4字符集,而t1表里面的code是utf8字符集,这里需要做字符集转换,字符集转换遵循由小到大的原则,因为utf8mb4是utf8的超集,所以这里把utf8转换成utf8mb4,即把t1.code转换成utf8mb4字符集,转换了之后,由于t1.code上面的索引仍然是utf8字符集,所以这个索引会被执行计划忽略了,然后t1表只能选择全表扫描。如果t2筛选出来的记录不止1条,那么t1就会被多次全表扫描,性能之差可想而知。

修改字符集

alter table t1 charset utf8mb4;

这是错的,这只是改了表的默认字符集,即新的字段才会使用utf8mb4,已经存在的字段仍然是utf8。

alter table t1 convert to charset utf8mb4;

查看 SHOW FULL COLUMNS FROM t1 ;会发现字段的字符已经改变。

其他

**排序规则概念:**是指对指定字符集下不同字符的比较规则。排序规则有以下特征:

  • 它和字符集(CHARSET)相关
  • 每种字符集都有多种它支持的排序规则
  • 每种字符集都会默认指定一种排序规则为默认值。
  • mysql可对相应字符集,单独设置排序规则

排序规则作用:排序规则指定后,它会影响我们使用 ORDER BY语句查询的结果顺序,会影响到 WHERE条件中大于小于号的筛选结果,会影响 DISTINCT、GROUP BY、HAVING 语句的查询结果。另外,mysql 建索引的时候,如果索引列是字符类型,也会影响索引创建,只不过这种影响我们感知不到。总之,凡是涉及到字符类型比较或排序的地方,都和排序规则有关。

注意点

  • 表字符集不同时,可能导致join的SQL使用不到索引,引起严重的性能问题;
  • 建表语句一定要设置默认字符集,需要关联的表字符集一定要统一;
  • 统一dev、fat、test、prod环境mysql对应库的字符集,可以及早发现问题;