服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|

服务器之家 - 数据库 - Mysql - 浅谈MySQL中的子查询优化技巧

浅谈MySQL中的子查询优化技巧

2020-05-05 16:50罗龙九 Mysql

这篇文章主要介绍了浅谈MySQL中的子查询优化技巧,子查询的优化是MySQL诸多优化方法中的基本,需要的朋友可以参考下

mysql的子查询优化一直不是很友好,一直有受业界批评比较多,也是我在sql优化中遇到过最多的问题之一,你可以点击这里 ,这里来获得一些信息,mysql在处理子查询的时候,会将子查询改写,通常情况下,我们希望由内到外,也就是先完成子查询的结果,然后在用子查询来驱动外查询的表,完成查询,但是恰恰相反,子查询不会先被执行;今天希望通过介绍一些实际的案例来加深对mysql子查询的理解:

案例:用户反馈数据库响应较慢,许多业务动更新被卡住;登录到数据库中观察,发现长时间执行的sql;

?
1
| 10437 | usr0321t9m9 | 10.242.232.50:51201 | oms | Execute | 1179 | Sending

Sql为:

?
1
2
3
4
select tradedto0_.* from a1 tradedto0_ where tradedto0_.tradestatus='1'
and (tradedto0_.tradeoid in (select orderdto1_.tradeoid from a2 orderdto1_ where
orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')) and tradedto0_.undefine4='1'
and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;

2.其他表的更新被阻塞:

?
1
2
update a1 set tradesign='DAB67634-795C-4EAC-B4A0-78F0D531D62F',
markColor=' #CD5555', memotime='2012-09- 22', markPerson='??' where tradeoid in ('gy2012092204495100032') ;

为了尽快恢复应用,将其长时间执行的sql kill掉后,应用恢复正常;
3.分析执行计划:

?
1
2
3
4
5
6
7
8
db@3306 :explain select tradedto0_.* from a1 tradedto0_ where tradedto0_.tradestatus='1' and (tradedto0_.tradeoid in (select orderdto1_.tradeoid
from a2 orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')) and tradedto0_.undefine4='1' and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;
+----+--------------------+------------+------+---------------+------+---------+------+-------+-----
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+------+---------------+------+---------+------+-------+-----
| 1 | PRIMARY | tradedto0_ | ALL | NULL | NULL | NULL | NULL | 27454 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | orderdto1_ | ALL | NULL | NULL | NULL | NULL | 40998 | Using where |
+----+--------------------+------------+------+---------------+------+---------+------+-------+-----

从执行计划上,我们开始一步一步地进行优化:
首先,我们看看执行计划的第二行,也就是子查询的那部分,orderdto1_进行了全表的扫描,我们看看能不能添加适当的索引:
A.使用覆盖索引:

?
1
2
db@3306:alter table a2 add index ind_a2(proname,procode,tradeoid);
ERROR 1071 (42000): Specified key was too long; max key length is 1000 bytes

添加组合索引超过了最大key length限制:
B.查看该表的字段定义:

?
1
2
3
4
5
6
7
8
9
db@3306 :DESC a2 ;
+---------------------+---------------+------+-----+---------+-------+
| FIELD        | TYPE     | NULL | KEY | DEFAULT | Extra |
+---------------------+---------------+------+-----+---------+-------+
| OID         | VARCHAR(50)  | NO  | PRI | NULL  |    |
| TRADEOID      | VARCHAR(50)  | YES |   | NULL  |    |
| PROCODE       | VARCHAR(50)  | YES |   | NULL  |    |
| PRONAME       | VARCHAR(1000) | YES |   | NULL  |    |
| SPCTNCODE      | VARCHAR(200) | YES |   | NULL  |    |

C.查看表字段的平均长度:

?
1
2
3
4
5
db@3306 :SELECT MAX(LENGTH(PRONAME)),avg(LENGTH(PRONAME)) FROM a2;
+----------------------+----------------------+
| MAX(LENGTH(PRONAME)) | avg(LENGTH(PRONAME)) |
+----------------------+----------------------+
|  95       |    24.5588 |

D.缩小字段长度

?
1
ALTER TABLE MODIFY COLUMN PRONAME VARCHAR(156);

再进行执行计划分析:

?
1
2
3
4
5
6
7
db@3306 :explain select tradedto0_.* from a1 tradedto0_ where tradedto0_.tradestatus='1' and (tradedto0_.tradeoid in (select orderdto1_.tradeoid from a2 orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')) and tradedto0_.undefine4='1' and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;
+----+--------------------+------------+-------+-----------------+----------------------+---------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+-------+-----------------+----------------------+---------+
| 1 | PRIMARY | tradedto0_ | ref | ind_tradestatus | ind_tradestatus | 345 | const,const,const,const | 8962 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | orderdto1_ | index | NULL | ind_a2 | 777 | NULL | 41005 | Using where; Using index |
+----+--------------------+------------+-------+-----------------+----------------------+---------+

发现性能还是上不去,关键在两个表扫描的行数并没有减小(8962*41005),上面添加的索引没有太大的效果,现在查看t表的执行结果:

?
1
2
db@3306 :select orderdto1_.tradeoid from t orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%';
Empty set (0.05 sec)

结果集为空,所以需要将t表的结果集做作为驱动表;
4.通过上面测试验证,普通的mysql子查询写法性能上是很差的,为mysql的子查询天然的弱点,需要将sql进行改写为关联的写法:

?
1
select tradedto0_.* from a1 tradedto0_ ,(select orderdto1_.tradeoid from a2 orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')t2 where tradedto0_.tradestatus='1' and (tradedto0_.tradeoid=t2.tradeoid ) and tradedto0_.undefine4='1' and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;

5.查看执行计划:

?
1
2
3
4
5
6
7
db@3306 :explain select tradedto0_.* from a1 tradedto0_ ,(select orderdto1_.tradeoid from a2 orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')t2 where tradedto0_.tradestatus='1' and (tradedto0_.tradeoid=t2.tradeoid ) and tradedto0_.undefine4='1' and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;
+----+-------------+------------+-------+---------------+----------------------+---------+------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+----------------------+---------+------+
| 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables |
| 2 | DERIVED | orderdto1_ | index | NULL | ind_a2 | 777 | NULL | 41005 | Using where; Using index |
+----+-------------+------------+-------+---------------+----------------------+---------+------+

6.执行时间:

?
1
2
db@3306 :select tradedto0_.* from a1 tradedto0_ ,(select orderdto1_.tradeoid from a2 orderdto1_ where orderdto1_.proname like '%??%' or orderdto1_.procode like '%??%')t2 where tradedto0_.tradestatus='1' and (tradedto0_.tradeoid=t2.tradeoid ) and tradedto0_.undefine4='1' and tradedto0_.invoicetype='1' and tradedto0_.tradestep='0' and (tradedto0_.orderCompany like '0002%') order by tradedto0_.tradesign ASC, tradedto0_.makertime desc limit 15;
Empty set (0.03 sec)

缩短到了毫秒;

延伸 · 阅读

精彩推荐
  • MysqlMySQL提示:The server quit without updating PID file问题的解决办法

    MySQL提示:The server quit without updating PID file问题的解决办法

    今天网站web页面提交内容到数据库,发现出错了,一直提交不了,数找了下原因,发现数据写不进去!第一反应,重启mysql数据库,一直执行中,停止不了...

    MYSQL教程网1692020-03-23
  • MysqlMysql中Insert into xxx on duplicate key update问题

    Mysql中Insert into xxx on duplicate key update问题

    在看代码的过程中碰到了这一用法,不太理解,google了一下。它的意义其实是如果在insert语句末尾制定了on duplicate key update语句的话,则当插入行会导致一...

    MYSQL教程网4592019-12-06
  • MysqlMySQL索引操作命令小结

    MySQL索引操作命令小结

    这篇文章主要介绍了MySQL索引操作命令小结,本文讲解了创建索引、查询索引、删除索引等内容,需要的朋友可以参考下 ...

    MYSQL教程网2982020-04-26
  • Mysql修改mysql5.5默认编码(图文步骤修改为utf-8编码)

    修改mysql5.5默认编码(图文步骤修改为utf-8编码)

    安装mysql后,启动服务并登陆,使用show variables命令可查看mysql数据库的默认编码;mysql数据库的默认编码并不是utf-8如何修改呢,本文将详细介绍,感兴趣的...

    MYSQL教程网3452019-12-16
  • Mysql为mysql数据库添加事务处理的方法

    为mysql数据库添加事务处理的方法

    开始首先说明一下,mysql数据库默认的数据库引擎是MyISAM,是不支持事务的,单数如果你添加了数据执行语句是不会出错的,单数不管用,即便是回滚事务,...

    MYSQL教程网2252019-11-21
  • Mysqlmysql登录遇到ERROR 1045问题解决方法

    mysql登录遇到ERROR 1045问题解决方法

    mysql登录时出现了错误:ERROR 1045: Access denied for user,究竟是什么原因呢?接下来为你详细介绍下,感兴趣的你可以参考下哈,或许可以帮助到你 ...

    MYSQL教程网2532019-12-19
  • MysqlMysql动态更新数据库脚本的示例讲解

    Mysql动态更新数据库脚本的示例讲解

    今天小编就为大家分享一篇关于Mysql动态更新数据库脚本的示例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟...

    执笔记忆的空白2362019-06-13
  • MysqlMySQL DBA 常用手册小结

    MySQL DBA 常用手册小结

    MySQL DBA 常用手册小结,使用mysql的朋友可以参考下。 ...

    MYSQL教程网3712019-11-27