一个 20 秒 SQL 慢查询优化处理方案

目录
  • 1.背景
  • 2.复杂SQL语句的构成
  • 3.关联查询
  • 4.子查询
  • 5.耗时在哪?
  • 6.问题定位
  • 7.初步断定
  • 9.再进一步验证
  • 10.解决方案
  • 11.另外一个需要注意的点

1.背景

页面无法正确获取数据,经排查原来是接口调用超时,而最后发现是因为SQL查询长达到20多秒而导致了问题的发生。
这里,没有高深的理论或技术,只是备忘一下经历和解读一些思想误区。

2.复杂SQL语句的构成

这里不过多对业务功能进行描述,但为了突出问题所在,会用类比的语句来描述当时的场景

复杂的SQL语句可以表达如下:

SELECT * FROM a_table AS a 
LEFT JOIN b_table AS b ON a.id=b.id 
WHERE a.id IN (
SELECT DISTINCT id FROM a_table 
WHERE user_id IN (100,102,103) GROUP BY user_id HAVING count(id) > 3
)

3.关联查询

从上面简化的SQL语句,可以看出,首先进行的是关联查询。

4.子查询

其次,是嵌套的子查询。此子查询是为了找出多个用户共同拥有的组ID。所以语句中的“100,102,103”是根据场景来定的,并且需要和后面“count(id) > 3”的个数对应。简单来说,就是找用户交集的组ID。

5.耗时在哪?

假设现在a_table表的数据量为20W,而b_table的数据量为2000W。大家可以想一下,你觉得主要的耗时是在关联查询部分,还是在子查询部分?
(思考空间。。。。)
(思考空间。。。。。。。)
(思考空间。。。。。。。。。。)

6.问题定位

对于SQL底层的原理和高深的理论,我暂时掌握不够深入。但我知道可以通过类比和简单的测试来验证是哪一块环节出了问题。

7.初步断定

首先,对于只有一个用户ID时,我会把上面的语句简化成:

ELECT * FROM a_table AS a 
LEFT JOIN b_table AS b ON a.id=b.id 
WHERE user_id IN (100)

所以,初步断定应该是嵌套的子查询部分占用了大部分的时间。

9.再进一步验证

既然定位到了是嵌套的子查询语句的问题,那又要分为两块待排查的区域:是子查询本身耗时大,还是嵌套而导致慢查询?
结果很容易发现,当我把子查询单独在DB中执行时,是非常快的。所以排除。
剩下的不言而喻,20秒的慢查询是嵌套引起的。

但因为处于上线紧急的过程中,为了确保,我快速地验证了我的结论:

  • 1、将子查询的ID单独执行,并把得到的结果序列手动拼成一段ID,如:1,2,3,4, … , 999
  • 2、将上面得到的序列ID,手动替换到原来的SQL语句
  • 3、执行,发现,很快!只用了约150 ms

Well Done!  准备修复上线!

10.解决方案

线上的问题,很多时间都是在定位问题和分析原因,既然问题找到了,原因也找到了,解决方案不言而喻。代码简单处理即可。

11.另外一个需要注意的点

当前,实际的SQL语句,会比这个更为复杂,但已足以表达问题所在。但在前期,笔者也做了一些SQL的代码。
因为b_tablea_table大,所以一开始b_table 左关联a_table 时,很慢,大概是1秒多,而且数据量是很少的;但若反过来,a_table 左关联b_table 时,则很快,大概是100毫秒。

所以,又发现一个有趣的现象:

大表 左关联 小表,很慢;小表 左关联 大表,很快。
当然,这些我们理论上都知道,但实际开发会忘却。又或者一开始两个表都为空时,而又没考虑到后期这两个表增长的速度时,日后就会埋下坑了。

总结:

首先,嵌套的子查询是很慢的。
原因,我还没仔细去研究,但在下班的路上和我的同事交流时,他说曾经看过这方面相关的书籍,是说每一次的子查询都会产生一个SQL语句,所以就N次查询了。而另外一位资深的QA同事则跟我说,应该是M*N的问题。
其次,我一开始使用嵌套子查询,是存在这样一个误区:我觉得将这些操作交给MySQL自身来处理会更高效,毕竟DB内部会有良好的机制来执行这些查询由。
然后,实际表白,我错了。因为这不是简单的合并MC批量查询。
当我们决定使用一些底层的技术时,只有当我们理解透彻了,才能使用更为恰当。而因为无知就断定工具、框架、底层无所不能时,往往就会中招。

到此这篇关于一个 20 秒 SQL 慢查询优化的经历与处理方案的文章就介绍到这了,更多相关 SQL 慢查询优化的经历与处理方案内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL优化方案之开启慢查询日志

    目录 前言 设置慢查询日志 测试 附:日志分析工具mysqldumpslow 总结 前言 本方案只适应于小的项目.项目未上线或者紧急情况下可采用这种方式,一旦开启慢日志查询会增加数据库的压力,所以一般采用后台对数据操作时间写入日志文件中,每一周定时进行清除日志 mysql优化方案:开启慢查询日志(查询sql执行超过一秒以上sql等等) 开启慢查询日志:可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能. 参数说明: slow_query_log

  • 一个 20 秒 SQL 慢查询优化处理方案

    目录 1.背景 2.复杂SQL语句的构成 3.关联查询 4.子查询 5.耗时在哪? 6.问题定位 7.初步断定 9.再进一步验证 10.解决方案 11.另外一个需要注意的点 1.背景 页面无法正确获取数据,经排查原来是接口调用超时,而最后发现是因为SQL查询长达到20多秒而导致了问题的发生.这里,没有高深的理论或技术,只是备忘一下经历和解读一些思想误区. 2.复杂SQL语句的构成 这里不过多对业务功能进行描述,但为了突出问题所在,会用类比的语句来描述当时的场景 复杂的SQL语句可以表达如下: S

  • SQL Server 性能调优之查询从20秒至2秒的处理方法

    一.需求 需求很简单,就是需要查询一个报表,只有1个表,数据量大约60万左右,但是中间有些逻辑. 先说明一下服务器配置情况:1核CPU.2GB内存.机械硬盘.Sqlserver 2008 R2.Windows Server2008 R2 SP1和阿里云的服务器,简单说就是阿里云最差的服务器. 1.原始表结构 非常简单的一张表,这次不讨论数据冗余和表字段的设计,如是否可以把Project和Baojian提出成一个表等等,这个是原始表结构,这个目前是没有办法改变的. 2.查询的sql语句为 sele

  • 一个简单的SQL 行列转换语句

    一个简单的SQL 行列转换 Author: eaglet 在数据库开发中经常会遇到行列转换的问题,比如下面的问题,部门,员工和员工类型三张表,我们要统计类似这样的列表 部门编号 部门名称 合计 正式员工 临时员工 辞退员工 1 A 30 20 10 1 这种问题咋一看摸不着头绪,不过把思路理顺后再看,本质就是一个行列转换的问题.下面我结合这个简单的例子来实现行列转换. 下面3张表 复制代码 代码如下: if exists ( select * from sysobjects where id =

  • 一个查询的SQL语句请教,希望能够用一条SQL语句得到结果

    各位老大:  我有一个查询的SQL语句请教,希望能够用一条SQL语句得到结果,问题如下: 表X:      A           B           C       D      ------------------      北京   广州   1000     10      北京   深圳   1200     20      北京   上海   500       30      北京   天津   300       40      北京   河南   800       20

  • 分享一个简单的sql注入

    所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句.比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到sql注入攻击. 分别输入1,2,3有对应的结果 使用'会报

  • 一个完整的SQL SERVER数据库全文索引的示例介绍

    首先,介绍利用系统存储过程创建全文索引的具体步骤: 1) 启动数据库的全文处理功能 (sp_fulltext_database) 2) 建立全文目录 (sp_fulltext_catalog) 3) 在全文目录中注册需要全文索引的表 (sp_fulltext_table) 4) 指出表中需要全文索引的列名 (sp_fulltext_column) 5) 为表创建全文索引 (sp_fulltext_table) 6) 填充全文目录 (sp_fulltext_catalog) ---------**

  • 一个有趣的SQL命题 用一条语句切换BIT型的真假值

    当然,第一反应是用存储过程.判断原来这个字段值,然后UPDATE. 网上粗粗找了一下没找到方案.自己一动手,居然有个很有趣的结果,连WHERE都不用 对数据库的BIT型赋值,用SQL语句来操作和用管理界面输入不同.管理界面输入必须用TRUE或FALSE来填写.而SQL语句则用0或1 于是乎,先尝试: 复制代码 代码如下: updat t set b=b+1 where id=1; select b from t where id=1; b字段一直保持1 那么用减号呢 复制代码 代码如下: upd

  • 基于Springboot一个注解搞定数据字典的实践方案

    目录 问题引出: 要求: 方案 实现 问题引出: 最近开了新项目,项目中用到了数据字典,列表查询数据返回的时候需要手动将code转换为name,到前台展示.项目经理表示可以封装一个统一的功能,避免程序员各自写各自的,代码混乱,风格不统一. 要求: 基于微服务架构,数据字典通过服务获取: 简化代码,使用简单: 使用Redis: 方案 大致的方向是自定义注解,在序列化的时候进行数据处理: 考虑到微服务,需要将主要逻辑放到common中,然后对外提供接口,各业务服务实现接口以获取字典数据: 考虑Red

  • 一个基于Asp.Net MVC的权限方案

    1.数据结构Mad_Popedom为权限表,Control记录控制器名,Action记录动作名.Mad_Role为角色表. 2.权限控制的实现此处使用比较简单AOP方式,用MVC的Filter实现,代码如下 复制代码 代码如下: using System.Collections.Generic; using System.Web.Mvc; using Madnet.Model.MadAdmin; using Madnet.BLL.MadAdmin; namespace Madnet.Contro

  • python反转一个三位整数的多种实现方案

    在LintCode上练习遇到这个问题,查阅资料找到多种方法,总结如下. 输入 输出 123 321 第一种:整数方法取余取整实现 class Solution: """ @param number: A 3-digit number. @return: Reversed number. """ def reverseInteger(self, number): g = number%10 #取出个位数 s = (int(number/10))%1

随机推荐