暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL优化器进化之路

恒生DBA公社 2021-04-21
422

【new friends】点击标题下面蓝色字“恒生DBA公社”关注。

【old friends】点击右上角,转发或分享本页面内容。


关注福利:

关注恒生DBA公社,回复18c,即可得到Oracle 18c outline官方手册




前 言


这几年,MySQL的版本在不断变更,使用的范围也越来越广,尤其的MySQL 5.5及之后的版本,可以说是数据库性能有了很大改善,今天主要跟大家一起探讨关于MySQL的优化器,5.7版本对比5.5版本的几点大的改进。当然,我会穿插一些小的实验来给大家更清晰直观的比较。


 MySQL针对子查询select的优化

MySQL 的子查询一直以性能差著称,之前的解决方案手工改写SQL使用关联join查询代替子查询。但是mySQL5.7版本中,子查询有了强劲的优化,也就是说,可以在不改变原有的SQL情况下,mySQL5.7 通过内部的优化器把子查询改成了关联查询。


比如通常情况下,我们希望在一个子查询中,优先完成内表里的查询结果,然后去驱动外查询的表,完成最终查询。但是在mySQL5.5及之前的版本里,子查询总是会先扫描外表中的所有数据,每条数据传到内表与之关联,如果外表很大,那么可想而知,性能将会很差。


 下面我们看一个例子

select orderNumber,

       productCode,

       quantityOrdered,

       priceEach,

       orderLineNumber

  from orderdetails o

 where o.ordernumber in

       (select orderNumber from orders d where d.ordernumber = 10423);


其中:orderdetails记录2996 条,orders 记录326条

我们希望SQL首先执行内表 oreders 把预定号码为10423的找出来,然后外表orderdetail只需匹配内表结果即可。但是mySQL 5.5版本中,数据库SQL是先把外表orderdetails全部查询一遍,然后再与内表order 进行一次select orderNumber from orders d whered.ordernumber=10423,所以性能低下。执行计划如下图:


而在MySQL 5.7的版本中,优化器使用semijoin=on的方式开启半连接子查询,通过优化器重写了SQL,实现了SQL由内向外的执行方式,而无需手工处理,也解决了之前子查询性能低的问题。

MySQL 5.7 此SQL的执行计划如下图:

对于MySQL 5.7,优化器默认半连接查询开启,可以通过showvariables like ‘optimizer_switch’\G来查看:如下图

 MySQL针对IN条件表达式的优化

MySQL 的子查询一直以性能差著称,之前的解决方案手工改写SQL使用关联join查询代替子查询。但是mySQL5.7版本中,子查询有了强劲的优化,也就是说,可以在不改变原有的SQL情况下,mySQL5.7 通过内部的优化器把子查询改成了关联查询。

 MySQL针对子查询select的优化

在mySQL 5.5数据库中,关于SQL中含有in的条件表达式,发现表达式中即使有索引,mySQL5.5优化器内部也会走全表扫描,无法用到索引。比如执行下面SQL语句时:

select productcode, productName, buyprice

  from products p

 where (productcode, buyprice) in

       (('S12_4473', '55.70'), ('S72_3212', '33.30'));


其中products的表结构如下,productCode 是有主键索引的


mySQL>show create table products\G;

***************************1. row ***************************

       Table: products

CreateTable: CREATE TABLE `products` (

  `productCode` varchar(15) NOT NULL DEFAULT ''COMMENT '产品代码',

  `productName` varchar(70) NOT NULL COMMENT '产品名称',

  `productLine` varchar(50) NOT NULL COMMENT '产品线',

  `productScale` varchar(10) NOT NULL,

  `productVendor` varchar(50) NOT NULL,

  `productDescription` text NOT NULL,

  `quantityInStock` smallint(6) NOT NULLCOMMENT '库存',

  `buyPrice` decimal(10,2) NOT NULL COMMENT '价格',

  `MSRP` decimal(10,2) NOT NULL COMMENT '建议零售价',

  PRIMARY KEY (`productCode`),

  KEY `productLine` (`productLine`),

  CONSTRAINT `products_ibfk_1` FOREIGN KEY(`productLine`) REFERENCES `productlines` (`productLine`)

)ENGINE=InnoDB DEFAULT CHARSET=utf8

1row in set (0.00 sec)


但是通过mySQL 5.5的执行计划看到,SQL执行以后仍然是全表扫描,并未用到主键索引。下图


而MySQL5.7版本中,该SQL已经被内部优化器优化,不在是全表扫描,执行计划改写为primary 的主键,速度当然也会更快。如图:


 MySQL针对Union all的优化

在MySQL5.5 版本数据库中,涉及union all的SQL查询,数据库内部优化器需要创建一张临时表,来实现量表的关联,但是这种方式会增大I/O的开销。看下面SQL执行计划:

Orderdetails表的查询结果和orders表的查询结果合并在了一张临时表里,然后返回输出给客户端。但是在MySQL5.7版本中,优化器不需要再创建一张临时表,而是直接依次将量表结果返回客户端,这样执行大大减少数据库关于I/O开销,提升了查询速度。

但是需要注意的是,union all对union和最外层使用orderby 不起作用。如图

 

 小结

这次我们主要讨论了MySQL 5.5和5.7两个版本,关于优化器的3点改进,分别是子查询select优化、IN条件表达式优化、unionall优化,当然MySQL 5.7 数据库性能方面的优化不止这些,比如优化器关于索引的改进、MySQL Innodb存储引擎的优化提升等等。大家一起学习,在以后的文章中我们会接着探讨。

文章转载自恒生DBA公社,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论