RDS最佳实践(四)—如何处理Mysql的子查询

早上值班同事在旺旺群里面贴了一条非常复杂的SQL,用户从本地迁移到RDS Mysql出现严重性能下降,同样的数据和表结构下,在本地的数据库上只要不到1s的时间,但是在rds上好几分钟都没响应。

碰到这类问题需要考虑以下一些因素:

a.数据库的版本不同(不同的版本优化器策略不一样,或者异构数据库间的迁移:oracle–>mysql,sqlserver–>mysql),导致sql执行计划不同,最后导致sql执行时间不同;

b.数据库的配置不同(不同的内存配置,参数设置–query cache是否打开),导致sql执行时间不同;

c.数据库的数据量不同(系统遇到bug,生成了大量的垃圾数据),导致sql执行时间不同;

根据以上线索,用户是刚刚从线下迁移到RDS的,所以数据量和表结构是相同的;

RDS配置为:2400M内存,1200IOPS,本地是笔记本电脑:4000M的内存,5600转的笔记本电脑,所以数据库配置来说区别并不大;

所以就剩下数据库版本了,RDS的版本是Mysql 5.5,而用户使用的数据库版本是5.6,所以问题很可能出现在这里,mysql 5.6和5.5在优化器上最大的改进就是对子查询的优化改进:

a.5.0、5.1、5.5对子查询处理:不会将子查询的结果集计算出来用作与其他表做join,所以很有可能outer 表每扫描一条数据,子查询都会被重新执行一遍,这样就导致性能下降;所以在5.5之前的版本中,处理子查询的问题通常采用sql改写:将子查询改写为join的方式;

b.5.6对子查询处理:将子查询的结果集cache到临时表里,临时表索引的主要目的是用来移除重复记录,并且随后也可能用于在做join时做查询使用,这种技术在5.6中叫做Subquery Materialize.物化的子查询可以看到select_type字段为SUBQUERY,而在MySQL5.5里为DEPENDENT SUBQUERY

5.5的执行计划:

mysql> explain select count(*) from test_pic as bi where bi.time in (select MAX(time) from test_pic where PIC_TYPE=1 GROUP BY BUILDING_ID) GROUP BY bi.BUILDING_ID;
+—-+——————–+————–+——-+—————+—————+———+——+——-+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+——————–+————–+——-+—————+—————+———+——+——-+————-+
| 1 | PRIMARY | bi | index | NULL | IDX_BPIC_0001 | 7 | NULL | 50226 | Using where |
| 2 | DEPENDENT SUBQUERY | test_pic | index | NULL | IDX_BPIC_0001 | 7 | NULL | 43 | Using where |
+—-+——————–+————–+——-+—————+—————+———+——+——-+————-+
2 rows in set (0.00 sec)

explain extended结果可以看到优化器的详细执行步骤采用exists的方式将外表与子查询的表关联起来,这样会大大增加子查询的执行频率:

select count(0) AS `count(*)` from `test_db`.`test_pic` `bi` where <in_optimizer>(`test_db`.`bi`.`time`,<exists>(select max(`test_db`.`test_pic`.`time`) from `test_db`.`test_pic` where (`test_db`.`test_pic`.`PIC_TYPE` = 1) group by `test_db`.`test_pic`.`BUILDING_ID` having (<cache>(`test_db`.`bi`.`time`) = <ref_null_helper>(max(`test_db`.`test_pic`.`time`))))) group by `test_db`.`bi`.`BUILDING_ID`

5.6的执行计划:

mysql> explain select count(*) from test_pic as bi where bi.time in (select MAX(time) from test_pic where PIC_TYPE = 1 GROUP BY BUILDING_ID) GROUP BY bi.BUILDING_ID;
+—-+————-+————–+——-+—————+—————+———+——+——-+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————–+——-+—————+—————+———+——+——-+————-+
| 1 | PRIMARY | bi | index | IDX_BPIC_0001 | IDX_BPIC_0001 | 7 | NULL | 46595 | Using where |
| 2 | SUBQUERY | test_pic | index | IDX_BPIC_0001 | IDX_BPIC_0001 | 7 | NULL | 46595 | Using where |
+—-+————-+————–+——-+—————+—————+———+——+——-+————-+
2 rows in set (0.00 sec)

explain extended结果可以看到优化器将子查询的结果集计算出来存放到一张临时表中,然后在与表做join:

/* select#1 */
select count(0) AS `count(*)` from `test56`.`test_pic` `bi` where <in_optimizer>(`test56`.`bi`.`time`,`test56`.`bi`.`time` in (
<materialize>
(/* select#2 */ select max(`test56`.`test_pic`.`time`) from `test56`.`test_pic`
where (`test56`.`test_pic`.`PIC_TYPE` = 1) group by `test56`.`test_pic`.`BUILDING_ID` having 1 ),
<primary_index_lookup>(`test56`.`bi`.`time` in <temporary table> on <auto_key>
where ((`test56`.`bi`.`time` = `materialized-subquery`.`MAX(time)`)))))
group by `test56`.`bi`.`BUILDING_ID`

所以针对该问题的解决方案就是将子查询改写为关联:

mysql> explain select count(*) from test_pic as bi , (select MAX(time) as time from test_pic where PIC_TYPE=1 GROUP BY BUILDING_ID) b where bi.time = b.time GROUP BY bi.BUILDING_ID;
+—-+————-+————–+——-+—————+—————+———+——+——-+———————————+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————–+——-+—————+—————+———+——+——-+———————————+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | Using temporary; Using filesort |
| 1 | PRIMARY | bi | ALL | NULL | NULL | NULL | NULL | 50226 | Using where; Using join buffer |
| 2 | DERIVED | test_pic | index | NULL | IDX_BPIC_0001 | 7 | NULL | 50226 | Using where |
+—-+————-+————–+——-+—————+—————+———+——+——-+———————————+
3 rows in set (0.06 sec)

explain extended的详细执行结果:

select count(0) AS `count(*)` from `test_db`.`test_pic` `bi` join (select max(`test_db`.`test_pic`.`time`) AS `time` from `test_db`.`test_pic` where (`test_db`.`test_pic`.`PIC_TYPE` = 1) group by `test_db`.`test_pic`.`BUILDING_ID`) `b` where (`test_db`.`bi`.`time` = `b`.`time`) group by `test_db`.`bi`.`BUILDING_ID`

SQL很快就执行得到结果;RDS已经推出5.6的版本,届时可以选择购买5.6的实例,同样也可以将5.5,5.1的实例升级到5.6,解决让人诟病的子查询性能问题。

PS.最佳实践:在oracle迁移到mysql的时候,请选用Mysql 5.6的版本,这样就可以避免麻烦的子查询改写了。

时间: 2024-10-03 11:14:18

RDS最佳实践(四)—如何处理Mysql的子查询的相关文章

生产库中遇到mysql的子查询

使用过oracle或者其他关系数据库的DBA或者开发人员都有这样的经验,在子查询上都认为数据库已经做过优化,能够很好的选择驱动表执行,然后在把该经验移植到mysql数据库上,但是不幸的是,mysql在子查询的处理上有可能会让你大失所望,在我们的生产系统上就由于碰到了这个问题: select  i_id, sum(i_sell) as i_sell from table_data where i_id in (select i_id from table_data where Gmt_create

mysql关联子查询的一种优化方法分析_Mysql

本文实例讲述了mysql关联子查询的一种优化方法.分享给大家供大家参考,具体如下: 很多时候,在mysql上实现的子查询的性能较差,这听起来实在有点难过.特别有时候,用到IN()子查询语句时,对于上了某种数量级的表来说,耗时多的难以估计.本人mysql知识所涉不深,只能慢慢摸透个中玄机了. 假设有这样的一个exists查询语句: select * from table1 where exists (select * from table2 where id>=30000 and table1.u

MySQL中表子查询与关联子查询的基础学习教程_Mysql

MySQL 表子查询表子查询是指子查询返回的结果集是 N 行 N 列的一个表数据. MySQL 表子查询实例 下面是用于例子的两张原始数据表: article 表: blog 表: SQL 如下: SELECT * FROM article WHERE (title,content,uid) IN (SELECT title,content,uid FROM blog) 查询返回结果如下所示: 该 SQL 的意义在于查找 article 表中指定的字段同时也存在于 blog 表中的所有的行(注意

【MySQL】子查询之一

MySQL 从版本4 开始支持 SQL 标准要求的所有子查询格式和操作,同时扩展了特有的几种特性.本文会介绍子查询的类型以及相关的注意点. 一 什么是子查询     子查询是将一个 SELECT 语句的查询结果作为中间结果,供另一个 SQL 语句调用.如: SELECT * FROM t1  WHERE vid in  (SELECT  vid FROM t2); 二 MySQL 子查询分类       根据子查询的返回数据形式,mysql 子查询可以分为以下几类: a  标量子查询      

MySQL的子查询中FROM和EXISTS子句的使用教程_Mysql

FROM 子查询FROM 子句中的子查询 MySQL FROM 子查询是指 FROM 的子句作为子查询语句,主查询再到子查询结果中获取需要的数据.FROM 子查询语法如下: SELECT ... FROM (subquery) AS name ... 子查询会生成一个临时表,由于 FROM 子句中的每个表必须有一个名称,因此 AS name 是必须的.FROM 子查询也称为衍生数据表子查询. FROM 子查询实例 table1: s1 s2 1 5 2 12 3 20 FROM 子查询 SQL

RDS最佳实践(一)–如何选择RDS

在去年双11之前,为了帮助商家准备天猫双11的大促,让用户更好的使用RDS,把RDS的性能发挥到最佳,保障双11当天面对爆发性增加的压力,不会由于RDS的瓶颈导致系统出现问题,编写了RDS的最佳实践.该文档的内容全部出自于生产实践,但由于篇幅的限制,我只是把其中的概要罗列到了ppt中,并没有展开详细的介绍,后续计划写一个系列,把ppt中的内容进一步展开来讲一讲,也算是对RDS用户的一个交代.   我该如何选择RDS?我要购买多大规格的RDS?RDS的连接数,iops指的是什么?上诉这些问题相信是

RDS最佳实践(二)—如何快速平稳的迁入RDS

在上一篇中大致介绍RDS的一些基本参数,包括数据库类型,版本,存储空间,规格:内存,连接数,io,地域等基本含义,本篇中将介绍如何快速平稳的迁入RDS. 用户在购买完RDS后,接下来就可以开始往RDS迁入数据了.在RDS刚刚对外提供服务的时候,用户只能通过将自己的数据库dump成为sql文件,然后再将sql文件source到RDS中去:数据迁移至RDS-MySQL之使用MySQLdump工具,数据迁移至RDS-SQLserver之利用SQL Server客户端工具,这两种方法是最简单的方法,但是

MySQL的子查询及相关优化学习教程_Mysql

一.子查询 1.where型子查询(把内层查询结果当作外层查询的比较条件) #不用order by 来查询最新的商品 select goods_id,goods_name from goods where goods_id = (select max(goods_id) from goods); #取出每个栏目下最新的产品(goods_id唯一) select cat_id,goods_id,goods_name from goods where goods_id in(select max(g

RDS最佳实践(五)—Mysql大字段的频繁更新导致binlog暴增

背景:RDS Mysql采用的binlog 格式默认为ROW,在Mysql 5.6的版本之前,Mysql每次列的修改(update)都需要记录表中所有列的值.这样就存在一个问题,如果表中包含很多的大字段,表的单行长度就会非常长,这样每次update就会导致大量的 binlog空间生成.针对这个问题,在mysql 5.6中进行了改进,复制支持"row image control" ,只记录修改的列而不是行中所有的列,这对一些包含 BLOGs 字段的数据来说可以节省很大的处理能力,因此此项