iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >故障分析 | MySQL 派生表优化
  • 782
分享到

故障分析 | MySQL 派生表优化

故障分析|MySQL派生表优化 2014-08-06 18:08:14 782人浏览 猪猪侠
摘要

一、问题 sql 原 SQL 如下: select name,count(name) from bm_id a left JOIN (select TaskName from up_pro_accept_v3_bdc

故障分析 | MySQL 派生表优化

一、问题 sql

原 SQL 如下:

select name,count(name) from bm_id a left JOIN
	(select TaskName from up_pro_accept_v3_bdc 
		uNIOn all select TaskName from up_pro_accept_v3_hsjs
		union all select TaskName from up_pro_accept_v3_hszjj
		union all select TaskName from up_pro_accept_v3_hzl
		union all select TaskName from up_pro_accept_v3_kjyw
		union all select TaskName from up_pro_accept_v3_kpzzzxwx
		union all select TaskName from up_pro_accept_v3_qdzc
		union all select TaskName from up_pro_accept_v3_rsj
		union all select TaskName from up_pro_accept_v3_sjba
		union all select TaskName from up_pro_accept_v3_spk
		union all select TaskName from up_pro_accept_v3_test
		union all select TaskName from up_pro_accept_v3_wygl
		union all select TaskName from up_pro_accept_v3_yms
		union all select TaskName from up_pro_accept_v3_zjj
		union all select TaskName from up_pro_accept_v3w) t 
	on  a.zxi = t.TaskName  group by name

这是一个统计类的 SQL,直接执行跑了好几个小时都没有结束,所以暂时不知道实际耗时,因为实在是太久了~

二、执行计划

老步骤,我们先看下执行计划,如下图:

这里 SQL 执行主要分为 2 个步骤:

  1. 顺序扫描每个 up_pro_accept 开头的子表数据,最终组成 t 表(派生表)
    • 扫描 t 表(派生表) 相关的所有子表,可以看到这里每张子表走的都是 全表扫描,有些表较大,有 100 多 w,检索较慢。
  2. a 表(bm_id)t 表(派生表) 进行关联查询,得到最后的结果.
    • t 表(派生表) 作为 被驱动表 大约 164W 行 左右,与 a 表做关联查询时走的是 全表扫描(ALL)a 表(bm_id) 作为 驱动表 大约 1.3W 行 左右,也就是说,表关联需要全表扫描 t 表(派生表) 1.3W 次,而每次都需要扫描 164W 行 数据,显然 SQL 的绝大部分时间其实都花在这一步上。

那么其实 SQL 优化也分为了 2 步,首先是多张子表的全表扫描,是否可以用索引扫描替换,加快数据检索。

而后是主要的环节,这个派生表作为被驱动表时,是否可以走索引?如果不能走索引,有没有其他方式减少 SQL 开销?

三、派生表

既然这个 SQL 优化涉及到了派生表,那么我们先看下何谓派生表,派生表有什么特性?

Derived table(派生表) 实际上是一种特殊的 subquery(子查询),它位于 SQL 语句中 FROM 子句 里面,可以看做是一个单独的表。

Mysql 5.7 之前的处理都是对 Derived table(派生表) 进行 Materialize(物化),生成一个 临时表 用于保存 Derived table(派生表) 的结果,然后利用 临时表 来协助完成其他父查询的操作,比如 JOIN 等操作。

mysql 5.7 中对 Derived table(派生表) 做了一个新特性,该特性允许将符合条件的 Derived table(派生表) 中的子表与父查询的表合并进行直接 JOIN,类似于 oracle 中的 子查询展开,由优化器参数 optimizer_switch="derived_merge=ON" 来控制,默认为 打开

但是 derived_merge 特性存在很多限制,当派生子查询存在以下操作时,该特性无法生效。DISTINCTGROUP BYUNION/UNION ALLHAVING关联子查询LIMIT/OFFSET 以及 聚合操作 等。

举个简单例子:

其中 a 表就是一个派生表

  1. 如果走 derived_merge 特性,那么可以走主键索引,速度非常快
  2. 如果关闭 derived_merge 特性,那么就会走全表扫描,速度非常慢
select * from (select * from up_pro_accept_v3_bdc) a 
where a.rowguid = "185c44aa-c23f-4e6f-bcd2-a38df16e2cc3"

四、SQL 优化

简单介绍了下派生表,下面我们开始尝试优化这个 SQL,步骤分 2 步:

  1. 解决多张派生子表 union all 时全表扫描的问题。
  2. 解决派生表在关联过程中无法使用索引的问题。

我们先解决问题 1,这个问题比较简单。

因为所有派生子表的查询都是 select TaskName from up_pro_accept_v3_xxx 类似这样,且外部关联字段也是 taskname,所以我们只要在对应表上建立 taskname 的索引即可。

建好索引后,我们再看下执行计划,所有的派生子表都走了 index 扫描,那么问题 1 基本解决了,但是由于 t 表(派生表) 在关联时还是走的全表扫描,并没有用到 derived_merge 特性,所以 SQL 还是非常非常慢(上万 s)。

接着我们来解决问题 2,这里主要解决派生表无法走索引的问题。

从之前介绍派生表的内容来看,想要派生表走索引,就需要用到 derived_merge 特性,将外部条件推入进子查询,但是这个特性的限制条件也很多,就比如我们这个 SQL,因为子查询里包括了 union all,那么该 SQL 是无法利用到 derived_merge 特性的,因此无法直接走索引过滤。

既然无法在原有 SQL 的基础上优化,那么我们只能考虑改写 SQL,通过 SQL 改写来达到优化的目的。

这里 SQL 其实是因为 驱动表 bm_id 最终是和派生表作表关联,导致无法利用索引,我们可以尝试将 驱动表 bm_id 也放到子查询中,只要前后语义是一致的,那么改写就没问题。这样就可以在子查询里就走完表关联,剩下的就是外部的分组排序,我们尝试下。


SELECT NAME
	,count(NAME)
FROM (
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_bdc bdc ON bm_id.zxi = bdc.TaskName
	UNION ALL 
	SELECT NAME FROM bm_id LEFT JOIN up_pro_accept_v3_hsjs hsjs ON bm_id.zxi = hsjs.TaskName
	UNION ALL 
	SELECT NAME FROM bm_id LEFT JOIN up_pro_accept_v3_hszjj hszjj ON bm_id.zxi = hszjj.TaskName
	UNION ALL 
	SELECT NAME FROM bm_id LEFT JOIN up_pro_accept_v3_hzl hzl ON bm_id.zxi = hzl.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_kjyw kjyw ON bm_id.zxi = kjyw.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_kpzzzxwx kp ON bm_id.zxi = kp.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_qdzc qdzc ON bm_id.zxi = qdzc.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_rsj rsj ON bm_id.zxi = rsj.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_sjba sjba ON bm_id.zxi = sjba.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_spk spk ON bm_id.zxi = spk.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_test test ON bm_id.zxi = test.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_wygl wygl ON bm_id.zxi = wygl.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_yms yms ON bm_id.zxi = yms.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3_zjj zjj ON bm_id.zxi = zjj.TaskName
	UNION ALL
	SELECT NAME	FROM bm_id LEFT JOIN up_pro_accept_v3w v3w ON bm_id.zxi = v3w.TaskName
	) t
GROUP BY t.name

再来看下改写后的 SQL 执行计划,发现确实如我们预想的,在子查询中可以通过索引来进行表关联(被驱动表 type 为 ref),然后 union all 汇聚数据,形成派生表,最后扫描派生表进行分组排序。

这里分组排序时只需要 全表扫描一次派生表 就可以得到结果,效率比之前快太多了!

改写后的 SQL 运行耗时为 13s 左右,速度快很多!

五、测试验证

为了严谨性,我们需要验证改写后的 SQL 结果集是否与原始 SQL 一致,也就是证明下这样改写 SQL 是否会产生语义上的变化,如果为了优化 SQL,连结果集都不准了,那就没意义了~

这里因为原始 SQL 执行太久,没法直接得到结果集对比,那么我们只能通过手动创建临时表来记录子查询结果集,然后再与 bm_id 表 关联查询,由于我们可以在临时表上创建索引,就不会出现原始 SQL 那种全表扫描的问题啦。

具体操作如下:

  1. 创建临时表(带索引)
CREATE TABLE `tmp_up` (
  `taskname` varchar(500) DEFAULT NULL,
  KEY `idx_taskname` (`taskname`));
  1. 将子查询结果插入至临时表
insert into tmp_up 
select taskname from up_pro_accept_v3_bdc 
union all select taskname up_pro_accept_v3_hsjs
......
  1. 使用临时表代替子查询
select name,count(name) from bm_id a left JOIN
			(select TaskName from tmp_up )t 
	on  a.zxi = t.TaskName  group by name
  1. 对比下查询结果是否一致

惊讶的发现改写 SQL 的结果集会多出来很多?这里可以确认走临时表的结果集是肯定没问题的,那么问题肯定出在改写 SQL 上!

回头再仔细想一下,结合小测试,发现这样改写 SQL 确实会改变语义,问题主要是出在 LEFT JOIN,原本 bm_id 只作了 一次表关联,而改写 SQL 后,要做 多次表关联,导致最后的结果集会多出来一部分因为 LEFT JOIN 而产生的重复数据。

如果是 INNER JOIN,其实就不会产生重复数据,我们也测试下,结果确实如所想,内联是没问题的~

六、个人总结

这次 SQL 优化案例个人感觉是比较有难度的,很多点自己一开始也没有想到。就比如 SQL 改写,一开始以为是没有语义上的区别,直到做了测试才知道,所以啊,很多时候不能盲目自信啊。

针对这个 SQL 来说,想要直接通过改写 SQL 优化还是比较难的,当然这里说的是不改变语义的情况下,我暂时没有想到好的改写方式,也许是火候还不够。

解决方式总结有 2 个:

  1. 内联 替代 左联,然后使用上述的改写 SQL,优点是 比较方便且查询速度较快,但是 结果集会变化
  2. 通过 临时表 代替 子查询,缺点是 比较繁琐,需要多个步骤实现,优点是 速度也较快结果集不会变化

附录:

Http://mysql.taobao.org/monthly/2017/03/05/ https://blog.csdn.net/sun_ashe/article/details/89522394 https://imysql.com/node/103 https://dev.mysql.com/doc/refman/5.7/en/derived-table-optimization.html

您可能感兴趣的文档:

--结束END--

本文标题: 故障分析 | MySQL 派生表优化

本文链接: https://www.lsjlt.com/news/4772.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL派生表联表查询的示例分析
    这篇文章给大家分享的是有关MySQL派生表联表查询的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。前情提要:公司运营的一个商城系统,忽然发现订单提现功能有问题,有大量的商户体现金额和订单金额不一致。于是产...
    99+
    2023-06-29
  • MySQL大表优化的示例分析
    小编给大家分享一下MySQL大表优化的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!背景阿里云RDS FOR MySQL(MySQL5.7版本)数据库业务表每月新增数据量超过千万,...
    99+
    2024-04-02
  • 分析MySQL优化思路
    这篇文章主要讲解了“分析MySQL优化思路”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“分析MySQL优化思路”吧!     ...
    99+
    2024-04-02
  • MySQL中怎么优化分表
    MySQL中怎么优化分表,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1、试验PROCEDURE.DELIMITER $$DROP...
    99+
    2024-04-02
  • Mysql优化之Zabbix分区优化的示例分析
    这篇文章主要介绍了Mysql优化之Zabbix分区优化的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。使用zabbix最大的瓶颈在于...
    99+
    2024-04-02
  • MySQL优化 - 性能分析与查询优化
    MySQL优化 - 性能分析与查询优化    优化应贯穿整个产品开发周期中,比如编写复杂SQL时查看执行计划,安装MySQL服务器时尽量合理配置(见过太多完全使用默认配置安装的情况),根...
    99+
    2024-04-02
  • mysql优化器追踪分析
    本篇内容主要讲解“mysql优化器追踪分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mysql优化器追踪分析”吧!以下 left joi...
    99+
    2024-04-02
  • MySQL如何实现分表优化
    这篇文章将为大家详细讲解有关MySQL如何实现分表优化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。   这里的分表逻辑是根据t_group表的user_nam...
    99+
    2024-04-02
  • Mysql slave延迟故障分析
    这篇文章主要介绍“Mysql slave延迟故障分析”,在日常操作中,相信很多人在Mysql slave延迟故障分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Mysql ...
    99+
    2024-04-02
  • MySQL limit性能分析与优化
    一、结论 语法结构: limit offset, rows 结论:rows 相同条件下,offset 值越大,limit 语句性能越差 二、测试 执行测试: 5750000 条数据 sql 1 执行时间:...
    99+
    2024-04-02
  • mysql hint优化的示例分析
    这篇文章将为大家详细讲解有关mysql hint优化的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。SELECT t.oldcontract...
    99+
    2024-04-02
  • 浅谈MySQL索引优化分析
    为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义。助你了解索引,分析索...
    99+
    2024-04-02
  • mysql优化之慢查询分析+explain命令分析+优化技巧总结
    分析慢查询 1.查看慢SQL是否启用,查看命令:show variables like 'log_slow_queries';  如果结果为ON则是开启了,如果为OFF则表示禁用了。 2.开启...
    99+
    2023-02-18
    mysql优化 mysql慢查询分析 mysqlexplain命令分析 mysql优化技巧总结
  • MySQL查询优化的示例分析
    小编给大家分享一下MySQL查询优化的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、优化的思路和原则有哪些1、 优化更需要优化的查询 2、 定位优化对象的性能瓶颈 3、 明确优...
    99+
    2024-04-02
  • my.ini与mysql优化的示例分析
    小编给大家分享一下my.ini与mysql优化的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!mysql优化了所以先说说...
    99+
    2024-04-02
  • Mysql优化策略的示例分析
    这篇文章主要介绍Mysql优化策略的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!总的来说:1、数据库设计和表创建时就要考虑性能2、sql的编写需要注意优化3、分区、分表、分...
    99+
    2024-04-02
  • mysql中limit优化的示例分析
    小编给大家分享一下mysql中limit优化的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!    ...
    99+
    2024-04-02
  • MySQL性能优化的案例分析
    这篇文章主要介绍MySQL性能优化的案例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!前言MySQL索引底层数据结构与算法MySQL性能优化原理-前篇实践(1)--MySQL性能...
    99+
    2024-04-02
  • MySQL中table_cache优化的示例分析
    这篇文章主要介绍MySQL中table_cache优化的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!table_cache指定表高速缓存的大小。每当MySQL访问一个表时,...
    99+
    2024-04-02
  • Mysql中USE DB堵塞故障分析
    这篇文章主要讲解了“Mysql中USE DB堵塞故障分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Mysql中USE DB堵塞故障分析”吧! ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作