隐式转换,可以说是关系型数据库SQL优化中很隐秘的问题,之前碰到过很多和他相关的案例,
《隐式转换的替代方案不是万能的》
《Oracle、SQL Server和MySQL的隐式转换异同》
《见识一下SQL Server隐式转换处理的不同》
《如何找到隐式转换的SQL?》
《从隐式转换案例,来挖掘开发人员的技能提升》
《浅谈显式转换和隐式转换》
《隐式转换的案例场景》
《一次有意思的错选执行计划问题定位》
数据和云的这篇文章《SQL优化——隐式字符编码转换》则介绍了MySQL中因为字符集不同导致的隐式转换的问题。
MySQL中我们知道,
- 如果对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。
- 隐式类型转换也会导致放弃走树搜索。
因为类型转换等价于在条件字段上使用了函数,例如,
代码语言:javascript复制假设tradeid字段有索引,且为varchar类型,
mysql> select * from tradelog where tradeid=110717;
等价于,
mysql> select * from tradelog where CAST(tradid AS signed int) = 110717;
下面来看看隐式字符编码转换导致的一个慢SQL,业务上有个SQL执行需要1.31秒,
看执行计划,
从执行计划分析看出问题出在r表也就是h_merge_result_new_indicator表全表扫描,查看该表的表结有联合索引。但是联合索引范围后会失效,于是打算新建一个联合索引,
查看预新建联合索引的字段选择性,
结合选择性来看,
代码语言:javascript复制create index idx_hmrni on h_merge_result_new_indicator(keyName,module,BATCH_NO);
创建后,再次看执行计划依然无效,
查看表结构,
另外3个表结构其中有2个utf8mb4,1个utf8,
字符集utf8mb4是utf8的超集,所以当这两个类型的字符串在做比较的时候,MySQL内部的操作是,先将utf8字符串转成utf8mb4字符集,再做比较。因此,
这部分会转换后再与h_merge_result_new_indicator关联。优化就只需要将字符集编码转为utf8再和h_merge_result_new_indicator关联就能用上索引,
再看查询只需要0.02秒了,
但是还有个问题,如上执行计划key_len是606=(100*3 3) (100*3 3)。就是说,没有用上BATCH_NO字段上的索引,我们知道索引少一个字段,占用会减少,不会太臃肿。因此,联合索引只需要包含(keyName,module),
代码语言:javascript复制drop index idx_hmrni on h_merge_result_new_indicator;
create index idx_hmrni on h_merge_result_new_indicator(keyName,module);
对索引字段做函数操作,无论是显式,还是隐式,都会破坏索引值的有序性,进而影响优化器的判断,这对Oracle和MySQL都是相通的。
因此,在开发阶段,以及审核阶段,对这些问题的规避和挖掘,还是值得关注的,投入产出比,还是比较可观的。