百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

说说 MySQL 子查询

wptr33 2024-11-17 16:43 20 浏览

前言

前两天开发找DBA解决一个含有子查询的慢sql,我们通过将其修改为关联查询和添加索引解决。考虑到 大多数开发并没有准确的理解 MySQL 的子查询执行原理。本文介绍如何解决子查询慢查的思路。

原理

首先 知其然,知其所以然。大部分子查询为什么慢?我们得了解 MySQL 关联查询和子查询的处理机制。

MySQL 在处理所有的查询的时候都强行转换为联接来执行,将每个查询包括多表中关联匹配,关联子查询,union,甚至单表的的查询都处理为联接,接着MySQL执行联接,把每个联接再处理为嵌套循环 (nest-loop);

很多使用子查询的人 想当然的认为 子查询会由内到外,先完成子查询的结果, 然后在用子查询来驱动外查询的表,完成查询。例如:select * from test where tid in (select aid from sub_test where gid=3)通常我们会想到该sql的执行顺序为:

a. 先从 sub_test 表中获取 gid=3的记录(3,4,5)

b. 然后和外面的查询做匹配 tid in (3,4,5)。

但是,实际上对于子查询,外部查询的每条符合条件的记录,都会把子查询执行一次。如果遇到子查询查询量比较大或者索引不合理的情况,sql就变慢查。

当我们使用explian查看包含子查询的执行计划时,尤其要注意select_type 字段的内容,如果包含 SUBQUERY , DEPENDENT SUBQUERY 就需要提高警惕。

官方含义为:

SUBQUERY:子查询中的第一个SELECT;

DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询 ,注意如果外部查询的结果集数量比较大,比如几十万上百万,就会执行几十万上百万次子查询,必然造成慢查。

优化策略

MySQL子查询优化策略大致分为:

  1. 半连接(semi-join): 半连接优化本质上是把子查询上拉到父查询中,与父查询的表做join/semi-join的操作。关键词上拉

  2. 物化子查询(Materialization):子查询的结果通常缓存在内存或临时表中。

  3. EXISTS strategy:把半连接转换为EXISTS操作。本质上是把父表的条件下推到子查询中关键词下推

一图胜千言 ,下图展示了 MySQL 针对子查询的优化策略

图片来自于 https://mariadb.com/kb/en/subquery-optimizations-map/

需要对图中做解释的是:

  1. 白色区域是常见的 子查询类型, x IN (SELECT ...) ,x= any(select),exists (select )。

  2. 白色区域越大说明使用频率越多,比如最常见的子查询是 x IN (SELECT ...)

  3. 有颜色的区域表示优化方法和策略,不同颜色代表不同的mysql 分支。

强烈安利 Mariadb 的一系列博客,里面有n篇文章介绍subquery的优化。阅读原文直达博客地址。

https://mariadb.com/kb/en/subquery-optimizations/

书上来的终觉浅,绝知此事要躬行。

优化案例

业务的sql 如下,该sql 执行超过1200ms ,被sql-killer kill掉,影响业务使用。

select app_name,pkg_version,zone,created_at 
from activity
where id in (
select MAX(id) AS id
from activity
where zone = 'qa' AND status = AND zanpkg_version != ''
AND namespace = 'qa'
group by app_name,zone)
order by id desc limit 500;

执行计划

第一步 MySQL 执行 select id, app_name,pkg_version,zone,created_at from activity order by id desc limit 500; 获取一个结果集

第二部 拿第一步中的结果500多行每一个记录去执行 子查询,每次遍历70w行左右。而且子查询里面没有合适的索引。

优化方法

1 where条件中zone=qa是固定值,group by zone 无意义,去掉group by zone。

2 针对 (zone, namespace, status) 加上组合索引。

3 改子查询为关联查询。

select a.app_name, a.zanpkg_version, a.zone, a.created_at 
from activity a, ( select MAX(id) AS mid
from activity
where zone = 'qa' AND status = 2 AND zanpkg_version != ''
AND namespace = 'qa'
group by app_name) b
where a.id = b.mid limit 500;

修改之后的sql执行时间在 300-500ms 之间。感觉还是慢,因为要对十几万的数据量做 聚合运算。

参考文章

https://www.cnblogs.com/zhengyun_ustc/p/slowquery3.html https://blog.csdn.net/kk185800961/article/details/49340589

https://blog.csdn.net/fly2nn/article/details/61924636

https://blog.csdn.net/fly2nn/article/details/61924637

https://blog.csdn.net/fly2nn/article/details/61924640

-The End-



相关推荐

Linux高性能服务器设计

C10K和C10M计算机领域的很多技术都是需求推动的,上世纪90年代,由于互联网的飞速发展,网络服务器无法支撑快速增长的用户规模。1999年,DanKegel提出了著名的C10问题:一台服务器上同时...

独立游戏开发者常犯的十大错误

...

学C了一头雾水该咋办?

学C了一头雾水该怎么办?最简单的方法就是你再学一遍呗。俗话说熟能生巧,铁杵也能磨成针。但是一味的为学而学,这个好像没什么卵用。为什么学了还是一头雾水,重点就在这,找出为什么会这个样子?1、概念理解不深...

C++基础语法梳理:inline 内联函数!虚函数可以是内联函数吗?

上节我们分析了C++基础语法的const,static以及this指针,那么这节内容我们来看一下inline内联函数吧!inline内联函数...

C语言实战小游戏:井字棋(三子棋)大战!文内含有源码

井字棋是黑白棋的一种。井字棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、三子旗等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时...

C++语言到底是不是C语言的超集之一

C与C++两个关系亲密的编程语言,它们本质上是两中语言,只是C++语言设计时要求尽可能的兼容C语言特性,因此C语言中99%以上的功能都可以使用C++完成。本文探讨那些存在于C语言中的特性,但是在C++...

在C++中,如何避免出现Bug?

C++中的主要问题之一是存在大量行为未定义或对程序员来说意外的构造。我们在使用静态分析器检查各种项目时经常会遇到这些问题。但正如我们所知,最佳做法是在编译阶段尽早检测错误。让我们来看看现代C++中的一...

ESL-通过事件控制FreeSWITCH

通过事件提供的最底层控制机制,允许我们有效地利用工具箱,适时选择使用其中的单个工具。FreeSWITCH是一个核心交换与混合矩阵,它周围有几十个模块提供各种功能特性。我们完全控制了所有的即时信息,这些...

物理老师教你学C++语言(中篇)

一、条件语句与实验判断...

C语言入门指南

当然!以下是关于C语言入门编程的基础介绍和入门建议,希望能帮你顺利起步:C语言入门指南...

C++选择结构,让程序自动进行决策

什么是选择结构?正常的程序都是从上至下顺序执行,这就是顺序结构...

C++特性使用建议

1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...

C++程序员学习Zig指南(中篇)

1.复合数据类型结构体与方法的对比C++类:...

研一自学C++啃得动吗?

研一自学C++啃得动吗?在开始前我有一些资料,是我根据网友给的问题精心整理了一份「C++的资料从专业入门到高级教程」,点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!个人...

C++关键字介绍

下表列出了C++中的常用关键字,这些关键字不能作为变量名或其他标识符名称。1、autoC++11的auto用于表示变量的自动类型推断。即在声明变量的时候,根据变量初始值的类型自动为此变量选择匹配的...