本文关键字:SQL 优化、字符集
一、布景
开发联络我,说是开发库上有一张视图查询速度很慢,9000 条数据要查 10s,要求我这边帮忙排查优化。
二、问题 SQL
Server version: 5.7.24-log MySQL Community Server (GPL)
这个 SQL 十分简略,界说如下,其中就引用了 view_dataquality_analysis这张视图,后边跟了两个 where 条件,而且做了分页。
SELECT * FROM view_dataquality_analysis WHERE modelguid = ‘710adae5-1900-4207-9864-d53ee3a81923’AND configurationguid = ‘6845d000-cda4-43ea-9fd3-9f9f1f22f95d’limit20;
咱们先去开发库上运转一下这条 SQL,下图中能够看到的确运转很慢,要 8s 左右。
三、履行计划
剖析一条慢 SQL,最有用的办法便是剖析它的履行计划,看是否存在问题。
下面咱们看下这条 SQL 的履行计划,主要由三张表(t、r、b)组成,从 t开端嵌套衔接 r,再嵌套衔接 b。整个履行逻辑很简略,至于 t、r、b 必定是视图中界说的表别号。
从履行计划中能够看出 t嵌套衔接 r 的时分走的是主键索引,可是持续嵌套衔接 b 的时分,却是走的全表扫描!那么或许很有或许问题就出在这个当地,为什么 b 表没有走索引,是由于缺失了索引吗?
四、视图剖析
带着上面履行计划中的疑问,咱们去看下 view_dataquality_analysis这张视图的界说,如下所示。
SELECT * FROM ( ( `dataquality_taskconfigurationhistory“t`LEFTJOIN`dataquality_rule“r`ON ((`t`.`RuleGuid` = `r`.`RuleGuid`)) ) LEFTJOIN`metadata_tablebasicinfo“b`ON ((convert(`b`.`TableGuid`using utf8mb4) = `r`.`Tableguid`)) )
视图由 dataquality_taskconfigurationhistory、 dataquality_rule和metadata_tablebasicinfo 表衔接组成,别离用了t、r、b 别号表明。由于都是用的 LEFT JOIN,所以表衔接次序应该是 t–>r–>b,和之前履行计划中显现的共同。
不知道各位有没有留意到
(convert(`b`.`TableGuid` using utf8mb4) = `r`.`Tableguid`))
这一段内容!表衔接上竟然存在一个字符集的转化。那么问题或许便是出在这儿。
起先我认为这一段字符集转化是开发在界说视图的时分自己加上去的,后来问询后发现开发并未如此做。测验将视图界说去掉这一段内容,可是发现保存后,这个转化却会主动生成!!!
五、表界说查看
带着上面的疑问,咱们去看下表界说,首先是 b表 metadata_tablebasicinfo,然后是 r 表dataquality_rule,最终是 t 表 dataquality_taskconfigurationhistory
CREATETABLE`metadata_tablebasicinfo` ( `TableGuid`varchar(50) NOTNULL, `SqlTableName`varchar(50) DEFAULTNULL, ……. PRIMARY KEY (`TableGuid`) ) ENGINE=InnoDBDEFAULTCHARSET=utf8 CREATETABLE`dataquality_rule` ( `RuleGuid`varchar(50) NOTNULL, `ModelGuid`varchar(50) DEFAULTNULL, ……. PRIMARY KEY (`RuleGuid`), KEY`idx_top` (`RuleGuid`,`Tableguid`) ) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 CREATETABLE`dataquality_taskconfigurationhistory` ( `RowGuid`varchar(50) NOTNULL, `ModelGuid`varchar(50) DEFAULTNULL, ……. PRIMARY KEY (`RowGuid`) ) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4
经过查看表界说后,能够看到 b表字符集为 utf8,而 t 表与 r 表字符集都为 utf8mb4!!!那么根本能够验证我的猜想,当 MySQL 创立视图时,假如发现表衔接字段字符集不相一起,会主动增加字符集转化。
别的之前咱们有个为什么 b 表没有走索引,是由于缺失了索引吗?的疑问。从上面 b表的表结构界说就能够看出, b 表的衔接字段为 TableGuid,是 b 表的主键,那么必定存在主键索引,就更不或许不走索引而挑选全表扫描了。
六、修正字符集
为了验证由于字符集问题而导致表衔接没有走索引,咱们挑选将 b表 metadata_tablebasicinfo 的字符集修正为 utf8mb4。
进程如下:
–修正表的默许字符集和一切列的字符集为utf8mb4 ALTER TABLE metadata_tablebasicinfo CONVERT TO CHARACTER SET utf8mb4;
经过上述操作后,现在 b表为 utf8mb4 字符集。
七、视图重建
将 b表字符集修正为 utf8mb4 后,去查看 view_dataquality_analysis 视图界说,发现仍是存在字符集转化,所以猜想这类主动增加转化的机制不会由于表结构更改而主动去掉。
咱们再次将视图中字符集转化的内容去掉后,保存视图,发现这次不会主动增加字符集转化。那么这次应该就应该会走索引啦~
咱们再次履行问题 SQL,履行时间为 0.2s,速度显着就正常了。
再来看一波履行计划,能够看到 b表上走的是主键索引,这下舒服了~
八、问题总结
经过这次问题排查,发现了字符集不同本来也会导致索引失效。其实这个问题有点相似于 int=varchar隐式转化问题,等号左面为 int 类型,右边为 varchar 类型,那么 MySQL 会主动转化类型为共同,因此无法走索引。
下次假如再呈现相似的问题,能够先查看下视图界说,假如存在字符集转化的内容,那么就能够查看是否是相似的问题!别的还有一个留意的点便是,列的字符集也有或许与表的字符集不同!