在mysql中,进行查询性能优化的时候,一般都是通过explain进行初步分析,确认是否应用了索引,扫描的数据等。但并不能显示sql执行的具体过程,需要了解sql的执行过程,只能进一步求助 optimizer_trace 了。不直接用 optimizer_trace 的原因是,optimizer_trace 必须完整的执行 SQL 之后,才能获取到所有有用的信息。
## 打开 optimizer_trace
set session optimizer_trace="enabled=on";
## 执行 SQL
select .....
## 查询 trace 结果
SELECT trace FROM information_schema.OPTIMIZER_TRACE;
通过 trace 结果我们发现,实际执行的 SQL 是:
SELECT
各种字段
FROM
`t_order_rel` `r`
JOIN `t_order` `s`
WHERE
(
( `r`.`order_id` = CONVERT ( `s`.`id` USING utf8mb4 ) )
AND ( `r`.`type` = 1 )
AND ( `r`.`share_code` = 'B2MTB6C' )
)
可以看到这里的?r.oder_id = s.id 条件已经变成了??`r`.`order_id` = CONVERT ( `s`.`id` USING utf8mb4 ),两个表的字段的编码是不一样的!导致 JOIN ON 的时候,套了一层编码转换?CONVERT ( s. id?USING utf8mb4 ))。 我们知道,字段外套一层函数这种条件匹配,是走不到索引的,例如:date(create_time) < "2021-8-1" ?是不能走索引的,但是?create_time < "2021-8-1" ?是可以的。不同类型之间列的比较,也走不到索引,因为 MySQL 会自动套上类型转换函数。这也是?MySQL 的语法糖经常带来的误用。
修改字段编码后,SQL 终于不是全扫描了。同时以后要注意:
- 数据库指定默认的编码,表不再指定默认编码,同时对于需要使用特殊编码的字段,针对字段指定编码
- join,where 的时候,注意 compare 两边的类型是否一致,是否会导致不走索引
优化举例:
原始代码:
select user_id from sys_user where user_id = 1;
优化后执行:
select `sys_user`.`user_id` AS `user_id` from `sys_user` where (`sys_user`.`user_id` = 1)
|