MySQL 的SQL引擎很差吗? 由一个同学提出问题引出的实验

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共2310人左右 1 + 2 + 3 + 4 +5+6) 新人奖直接分配到5群,5群已经停止进行自由申请 已建立6群(接近100)。

12039f38100963dbf34a93c166fd6e4a.png

有了PostgreSQL的出现,MySQL的数据库在SQL的处理上的问题一直被人当做有意思的事情来去谈论,实际上每种数据库有自己不同的个性,我们掌握就好,无需特别的进行一些情感上的好恶。

MySQL 最近一个同学给我提了一个问题,关于为什么一个简单的语句,并且语句中提取的数据是唯一的一条,而却在下面循环了三次,因为我没有他的数据,也仅仅是看了执行计划和语句,又因为是截图,所以只是简单的看了看。

不过今天转念一想,到底MySQL 8 的数据处理部分,基于我之前在MySQL 5.6 5.7等积累的一些关于 子查询差的口碑,是否被改善了的事情又重新让我想起了,8.0 是不是更好,随即使用了8.031版本的MySQL 数据库。

21c9bb0b2df021dee5eeaf5bef7bd336.png

这里我们使用了一个MYSQL 的emaple数据库,并编造了一段可以重新写成两种方式的SQL ,我们直接来看一下.

select o.*
from orders as o 
left join orderdetails as d on o.ordernumber = d.ordernumber and o.ordernumber = (select ordernumber  
from orders where requiredDate > '2003-01-12' and requiredDate < '2003-01-14')
where o.requiredDate > '2003-01-12' and o.requiredDate < '2003-01-14';

| -> Nested loop left join  (cost=1.36 rows=4) (actual time=0.070..0.075 rows=4 loops=1)
    -> Index range scan on o using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14'), with index condition: ((o.requiredDate > DATE'2003-01-12') and (o.requiredDate < DATE'2003-01-14'))  (cost=0.71 rows=1) (actual time=0.060..0.062 rows=1 loops=1)
    -> Filter: ((o.orderNumber = (select #2)) and (d.orderNumber = (select #2)))  (cost=0.65 rows=4) (actual time=0.009..0.011 rows=4 loops=1)
        -> Covering index lookup on d using PRIMARY (orderNumber=(select #2))  (cost=0.65 rows=4) (actual time=0.007..0.010 rows=4 loops=1)
        -> Select #2 (subquery in condition; run only once)
            -> Filter: ((orders.requiredDate > DATE'2003-01-12') and (orders.requiredDate < DATE'2003-01-14'))  (cost=0.46 rows=1) (actual time=0.028..0.032 rows=1 loops=1)
                -> Covering index range scan on orders using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14')  (cost=0.46 rows=1) (actual time=0.027..0.030 rows=1 loops=1)
        -> Select #2 (subquery in condition; run only once)
            -> Filter: ((orders.requiredDate > DATE'2003-01-12') and (orders.requiredDate < DATE'2003-01-14'))  (cost=0.46 rows=1) (actual time=0.028..0.032 rows=1 loops=1)
                -> Covering index range scan on orders using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14')  (cost=0.46 rows=1) (actual time=0.027..0.030 rows=1 loops=1)
 |

第二个写法

select o.*
from orders as o 
left join orderdetails as d on o.ordernumber = d.ordernumber 
left join orders as o2 on o2.ordernumber = d.ordernumber and o2.requiredDate > '2003-01-12' and o2.requiredDate < '2003-01-14' 
where o.requiredDate > '2003-01-12' and o.requiredDate < '2003-01-14';

| -> Nested loop left join  (cost=5.11 rows=9) (actual time=0.039..0.047 rows=4 loops=1)
    -> Nested loop left join  (cost=1.89 rows=9) (actual time=0.034..0.039 rows=4 loops=1)
        -> Index range scan on o using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14'), with index condition: ((o.requiredDate > DATE'2003-01-12') and (o.requiredDate < DATE'2003-01-14'))  (cost=0.71 rows=1) (actual time=0.023..0.025 rows=1 loops=1)
        -> Covering index lookup on d using PRIMARY (orderNumber=o.orderNumber)  (cost=1.18 rows=9) (actual time=0.009..0.012 rows=4 loops=1)
    -> Filter: ((o2.requiredDate > '2003-01-12') and (o2.requiredDate < '2003-01-14'))  (cost=0.26 rows=1) (actual time=0.001..0.002 rows=1 loops=4)
        -> Single-row index lookup on o2 using PRIMARY (orderNumber=d.orderNumber)  (cost=0.26 rows=1) (actual time=0.001..0.001 rows=1 loops=4)

从下面的截图语句,可以看到两种写法的语句均得到一样的逻辑数据结果,但是相关的语句的执行计划完全不同。

dffb038f4c3a1f3628bffb3737d85495.png

1 子查询类型的方式撰写的语句

select o.*

from orders as o 

left join orderdetails as d on o.ordernumber = d.ordernumber and o.ordernumber = (select ordernumber  

from orders where requiredDate > '2003-01-12' and requiredDate < '2003-01-14')

where o.requiredDate > '2003-01-12' and o.requiredDate < '2003-01-14';

从上面的语句中可以看到,将其中一个left join中的条件对接到子查询中,这里可以看到子查询得出的数据一定是一条数据,否则语句就会报错。

但从执行计划,我们明显可以看出,对于日期的部分,在执行计划中,出现了三次。

其中的两次是一致的,

 -> Select #2 (subquery in condition; run only once)

            -> Filter: ((orders.requiredDate > DATE'2003-01-12') and (orders.requiredDate < DATE'2003-01-14'))  (cost=0.46 rows=1) (actual time=0.028..0.032 rows=1 loops=1)

                -> Covering index range scan on orders using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14')  (cost=0.46 rows=1) (actual time=0.027..0.030 rows=1 loops=1)

        -> Select #2 (subquery in condition; run only once)

            -> Filter: ((orders.requiredDate > DATE'2003-01-12') and (orders.requiredDate < DATE'2003-01-14'))  (cost=0.46 rows=1) (actual time=0.028..0.032 rows=1 loops=1)

                -> Covering index range scan on orders using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14')  (cost=0.46 rows=1) (actual time=0.027..0.030 rows=1 loops=1)

 |

出现这样的情况主要是因为,在MySQL 在通过子查询查询数据的时候,会在子查询内部重新评估条件,并产生相关的执行计划,导致重复的查询计划生成的操作,同时由于查询的复杂度,尤其子查询的嵌入会增加优化器生成和选择执行计划的难度,在这样的情况下优化器会多次进行过滤,还有在MySQL在进行执行计划产生的时候会尝试不同的执行计划并在其中做出优化策略的权衡时为保证查询结果的正确性,产生多次过滤方式保证最终结果的正确性。

而反观我们不在使用子查询撰写的语句在执行计划中并未有重复的日志数据的过滤,并且从执行时间上看,也要比子查询的方式查询的速度要快。

select o.*

from orders as o 

left join orderdetails as d on o.ordernumber = d.ordernumber 

left join orders as o2 on o2.ordernumber = d.ordernumber and o2.requiredDate > '2003-01-12' and o2.requiredDate < '2003-01-14' 

where o.requiredDate > '2003-01-12' and o.requiredDate < '2003-01-14';

| -> Nested loop left join  (cost=5.11 rows=9) (actual time=0.039..0.047 rows=4 loops=1)

    -> Nested loop left join  (cost=1.89 rows=9) (actual time=0.034..0.039 rows=4 loops=1)

        -> Index range scan on o using idx_requiredDate over ('2003-01-12' < requiredDate < '2003-01-14'), with index condition: ((o.requiredDate > DATE'2003-01-12') and (o.requiredDate < DATE'2003-01-14'))  (cost=0.71 rows=1) (actual time=0.023..0.025 rows=1 loops=1)

        -> Covering index lookup on d using PRIMARY (orderNumber=o.orderNumber)  (cost=1.18 rows=9) (actual time=0.009..0.012 rows=4 loops=1)

    -> Filter: ((o2.requiredDate > '2003-01-12') and (o2.requiredDate < '2003-01-14'))  (cost=0.26 rows=1) (actual time=0.001..0.002 rows=1 loops=4)

        -> Single-row index lookup on o2 using PRIMARY (orderNumber=d.orderNumber)  (cost=0.26 rows=1) (actual time=0.001..0.001 rows=1 loops=4)

通过此示例我们可以得出如下的一些结论

1  在MySQL中如果可以将子查询改为连接查询的,尽量改为连接查询 ,通常连接查询的被正确翻译并走更优的执行计划的可能性更高。

2  左连接通常比子查询更适合大数据量的情况,子查询会产生中间结果集,导致内存压力增大,和查询性能的下降,左连接可以更好的解决由于子查询在产生的子结果集较大时产生的问题,减少中间结果集的产生,提高执行效率。

这同时也体现了,MySQL SQL 处理引擎,在SQL的解析上应还有更多的进步的空间,在SQL转换为内部数据结果时,对于子查询上的语义的解读上还有改善的空间。

同时本公众号,正在举办留言送书活动,详情请到下面的页面查看具体活动和送书的具体细节https://mp.weixin.qq.com/s/FOVBTcShVgwQJgZ2FrHBSQ

置顶文章:

临时工访谈:我很普通,但我也有生存的权利,大龄程序员 求职贴

临时工说:DBA 是不是阻碍国产数据库发展的毒瘤 ,是不是?从国产DB老专家的一条留言开始

震惊了:TechTalk技术交流社区主理人荐书 | 我与 Zabbix 的故事

临时工说:DBA 新职业,善于发现工作的人有工作---云数据库成本精算师

往期热门文章:

PolarDB  Serverless POC测试中有没有坑与发现的疑问

临时工访谈:庙小妖风大-PolarDB 组团镇妖 之 他们是第一

MongoDB 不是软柿子,想替换就替换

临时工访谈:PolarDB  Serverless  发现“大”问题了  之 灭妖记 续集

感谢 老虎刘 刘老师 对 5月20日 SQL 问题纠正贴 ---PostgreSQL 同一种SQL为什么这样写会提升45%性能

PostgreSQL 同一种SQL为什么这样写会提升45%性能 --程序员和DBA思维方式不同决定

临时工访谈:金牌 “女” 销售从ORACLE 转到另类国产数据库 到底  为什么?

PostgreSQL  熊灿灿一句话够学半个月 之 KILL -9

MongoDB  挑战传统数据库聚合查询,干不死他们的

临时工访谈:无名氏意外到访-- 也祝你好运(管理者PUA DBA现场直播)

临时工说:搞数据库 光凭的是技术,那DBA的死多少次?

PostgreSQL  分组查询可以不进行全表扫描吗?速度提高上千倍?

临时工说:分析当前经济形势下 DBA 被裁员的根因

JunkFood读者说你文章不对,作者被鞭策后,DBA 开始研究JAVA程序锁

PostgreSQL PG_DUMP 工作失败了怎么回事及如何处理

MySQL 八怪(高老师)现场解决问题实录

临时工说:经济规律解读ORACLE 工资低   --读 Roger 数据库专栏

PostgreSQL 为什么也不建议 RR隔离级别,MySQL别笑

临时工访谈:OceanBase上海开大会,我们四个开小会 OB 国产数据库破局者

临时工说:OceanBase 到访,果然数据库的世界很卷,没边

临时工访谈:恶意裁员后,一个国产数据库企业程序员的心声

临时工访谈:国产数据库裁员失业中,但我仍然积极乐观的DBA

临时工访谈:45岁IT女领导 失业 后的人生下半部

临时工访谈:TM 这些年 我都培训了什么

临时工说:上云后给 我一个 不裁 DBA的理由

临时工说:腾讯云,阿里云故障  “核爆炸”  后持续的影响

临时工说:三次封禁后的文章--技术文章怎么写,我有罪

PolarDB for PostgreSQL  有意思吗?有意思呀

PostgreSQL   玩PG我们是认真的,vacuum 稳定性平台我们有了

临时工说:裁员裁到 DBA 咋办  临时工教你 套路1 2 3

PolarDB  搞那么多复杂磁盘计费的东西,抽筋了吗?

临时工说:OceanBase 到访,果然数据库的世界很卷,没边

MONGODB  ---- Austindatabases  历年文章合集

MYSQL  --Austindatabases 历年文章合集

POSTGRESQL --Austindatabaes 历年文章整理

POLARDB  -- Ausitndatabases 历年的文章集合

PostgreSQL  查询语句开发写不好是必然,不是PG的锅

SQL SERVER 如何实现UNDO REDO  和PostgreSQL 有近亲关系吗

MongoDB 2023纽约 MongoDB 大会 -- 我们怎么做的新一代引擎 SBE Mongodb 7.0双擎力量(译)

MongoDB 2023年度纽约 MongoDB 年度大会话题 -- MongoDB 数据模式与建模

MongoDB  双机热备那篇文章是  “毒”

MongoDB   会丢数据吗?在次补刀MongoDB  双机热备

临时工说:从人性的角度来分析为什么公司内MySQL 成为少数派,PolarDB 占领高处

POLARDB  到底打倒了谁  PPT 分享 (文字版)

PostgreSQL  字符集乌龙导致数据查询排序的问题,与 MySQL 稳定 "PG不稳定"

PostgreSQL  Patroni 3.0 新功能规划 2023年 纽约PG 大会 (音译)

Austindatabases 公众号,主要围绕数据库技术(PostgreSQL, MySQL, Mongodb, Redis, SqlServer,PolarDB, Oceanbase 等)和职业发展,国外数据库大会音译,国外大型IT信息类网站文章翻译,等,希望能和您共同发展。

fb5abfdc8523c4cb3dfeba5d8167226f.png

  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值