慢SQL治理方法论

@

一、背景

从业务的角度来看:慢SQL会导致产品用户体验差,会减低用户对产品的好感度。
数据库的角度来看:慢SQL会影响数据库的性能,每个SQL执行都需要消耗一定的I/O资源。假设总资源是100,有一条慢SQL占用了30的资源共计1分钟。那么在这1分钟时间内,其他SQL能够分配的资源总量就是70,如此循环,当资源分配完的时候,所有新的SQL执行将会排队等待。

二、发现

在治理慢SQL前我们需要知道哪些SQL是慢SQL,即明确治理的对象。MySQL本身提供了慢查询日志,当SQL耗时超过指定 阈值的时候,会将SQL记录到慢查询日志文件中,用户能够从慢查询日志文件中提取出慢SQL。
MySQL是可以动态开启慢查询日志,即线上的服务器没有开启慢日志,重启后会失效。为防止线上业务受影响,可以先这样修改,同时将my.cnf配置文件补上配置项即可。

  • 查看配置
  • slow_query_log 是否启用慢查询日志
  • long_query_time 慢查询阈值
  • slow_query_log_file 慢查询日志文件slow.log位置
show VARIABLES like '%query%';
  • 开启慢查询日志

MySQL数据库默认不启动慢查询日志,需要手动设置,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响

开启慢查询日志
set global slow_query_log=ON;

慢查询阈值
set global long_query_time=1;

慢查询日志文件
set global slow_query_log_file=/tmp/mysql_slow.log

三、定位

我们通过慢查询日志提取出慢SQL,将这些慢SQL按不同的应用进行区分并整理一份文档,再定位到对应应用的代码,在文档中记录慢SQL应用在什么业务中,运行在什么场景中(定时任务、在线实时查询等)。

慢SQL治理方法论

四、分析

接下来是根据整理的文档,对这些慢SQL做一些分析,找出慢SQL产生的原因。

4.1 索引层面分析

使用explain命令输出SQL的执行计划,透过执行计划我们可以了解慢SQL的执行细节。

Mysql中的执行计划各列说明。

  • id: 按照sql语法解析后分层后的编号
  • type:执行计划中指定表使用的访问路径方式。

这是个 非常重要的字段,也是我们判断一个SQL执行效率的主要依据(以下只列举常见的几种)。
依次从最优到最差分别为: system > const > eq_ref > ref > range > index > ALL

  • system:从系统表读一行。这是const联接类型的一个特例。
  • const:表最多有一个匹配行,它将在查询开始时被读取。const用于常数值比较PRIMARY KEY或UNIQUE索引的所有部分

慢SQL治理方法论
  • eq_ref:它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY。

慢SQL治理方法论
  • ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。
  • ref是我们日常开发中较为常见的情况,也是原则上期望要达到的级别,查询命中到索引。

慢SQL治理方法论
  • range:索引范围扫描

慢SQL治理方法论
  • index:只扫描索引树,不需要回表查询。在这种情况下,explain 的 Extra 列的结果是 Using index

慢SQL治理方法论
  • all:全表扫描。

  • possible_keys:查找表中的行时可选择的索引。

  • key:显示MySQL实际决定使用的索引。
  • key_len:显示MySQL使用索引键的长度,就是此次查询所选择的索引长度有多少字节。
  • ref:ref字段标识哪些字段或者常量被用来和key配合从表中查询记录出来,如果为NULL表示没有
  • rows:该列表明MySQL估计要读取并检查的行数,需要注意的是, 这个不是结果集里的行数
  • filtered:它指返回结果的行数(MySQL层where过滤生效的数据量)占需要扫描到的行数(rows列的值)的百分比,一般来说越高越好,越低证明查询代价越高。
  • Extra:该列包含MySQL解决查询的详细信息(以下只列举常见的几种)。
  • Using filesort:说明mysql会对数据使用一个 外部的索引排序,而不是按照表内的索引顺序进行读取。
  • Using index:这个值重点强调了只需要使用索引就可以满足查询表的要求,不需要直接访问表数据了,一般表示使用了 覆盖索引
  • Using temporary:这个值表示使用了内部 临时表(基于内存的表)。这种情况通常发生在查询时包含了group by和order by子句,或者来自不同表的列使用了distinct。
  • Using where:where条件查询,通常using where表示优化器需要通过索引 回表查询数据

4.2 业务层面分析

根据具体业务场景考虑

[En]

Consider according to the specific business scenario

  • 查询条件是否都是必须的
  • 查询时间范围可否缩短
  • 表中有一些大字段根据实际情况缩短字段长度
    [En]

    there are some large fields in the table to shorten the field length according to the actual situation.*

  • 如果不是每次都必须获取的话,可以考虑从当前表拆出去,主表都是小字段,子表维护大字段,这样效率会更高

五、解决

5.1 SQL优化

5.1.1索引优化

  1. 左前缀原则:索引了多个列时,查询时必须从最左列开始,不能跳过,否则索引失效
  2. 在使用不等于符号时(!=,<>)会索引失效
  3. 使用is not null会索引失效,但is null 不会
  4. like模糊查询中以通配符开头会索引失效
  5. 使用or时,左右两边的字段都需要加上索引,否则索引失效
  6. 在索引列上使用函数会索引失效
  7. 避免隐式类型转换-字符串类型字段不加单引号索引失效
  8. 有时候MySQL优化器选择的索引不一定是最优的,可以通过 FORCE INDEX(idx_order_id)强制要求走某个索引,当然,必须保证这个索引以后不能被删除,不然就是个BUG

5.1.2 子查询优化

select something from user_table
where id in (select user_id from order_table where xxx=yyyy);

MySQL从5.7开始优化器对子查询进行了优化,会自动转换为join再执行,而对于5.7以下版本的MySQL 我们建议把子查询改成join的方式:

 select a.something
 from user_table a, order_table b
 where a.id=b.user_id
 and b.xxx=yyyy;

5.1.3 分页优化

Limit中分页查询会随着pageNo增大而变缓慢,通过子查询+表连接解决
select * from mytbl order by id limit 100000,10 改进后的 SQL语句如下:

select * from mytbl
where id >= ( select id from mytbl order by id limit 100000,1 )
limit 10
或者
select * from mytbl
inner join (select id from mytbl order by id limit 100000,10) as tmp
on tmp.id=ori.id;

5.1.4 Using filesort文件排序优化

orders建立了idx_ppo_created_at索引,使用EXPLAIN进行分析

EXPLAIN
SELECT id,
       temp_id,
       pos_id,
       type,
       member_id,
       temp_status,
       money_amount,
       trans_amount,
       return_trans_amount,
       coupon_id,
       cash_points,
       is_cancel,
       is_auto_cancel,
       is_compensate,
       is_multi_equity,
       company_id,
       store_id,
       store_type,
       source_orders,
       return_order_id,
       created_at,
       updated_at
FROM orders
WHERE 1 = 1
  AND created_at >= '2021-08-27 00:00:00'
  AND created_at

慢SQL治理方法论
我们可以看到Extra列出现了Using filesort,说明MySQL会对数据使用一个外部的索引排序, 而不是按照表内索引顺序进行读取。

解决

因为索引的叶子节点数据是根据 created_at 有序的,我们可以利用这一点来避免排序。
我们将 ORDER BY id DESC 换成 ORDER BY created_at DESC,查看执行计划,Using filesort 已消失:

慢SQL治理方法论

5.2 业务改造

如果SQL本身的性能已经到达极限了,但是耗时仍然很长。这时候,我们可以业务角度着手,看看在业务上能不能做一些变通、妥协。

5.2.1 总量显示优化

慢SQL治理方法论
如上图所示,我们在做分页时,页底都会展示符合条件的记录总数,以及分页页数。数据量少的时候,不会带来性能问题,但如果数据量较大,这个计算总量的count() 本身就不会太快,再加上每次打开页面都要计算一次的话,那这样会就带性能问题了,同时也会拖慢页面打开速度。
如果业务上允许,当数据量少时,精确显示,当数据量过大后,用户对真实数据不敏感时,那我们就可以通过 1000+ 等模糊的方式进行初步显示,减少不必要的扫描,同时也可以让用户首次打开时达到加速效果。
原SQL:select count(
) from table where xxx=yyy;
调整后:select count(*) from (select id from table where xxx=yyy limit 1000);

5.2.1 关联表优化

由于业务很复杂,某条SQL关联了很多表,导致表关联时的匹配耗时很长。这时候可以看看能不能将多关联SQL改成较少表的关联,使用代码方式进行关联,但是会增加请求次数。

5.3 减少数据

如果单表行数超过500万行或者单表容量超过2GB,SQL再怎么优化还是会慢,这个时候就要做数据拆分,这属于架构层面的变动,影响的面很大,除了慢SQL本身之外,其他的相关SQL也可能会被”波及”到。这种慢SQL治理的手段,能够一定程度上解决慢SQL问题。减少作用数据的方式有:

  • 垂直拆分
  • 水平拆分
  • 综合拆分(垂直+水平)

总结

本文介绍了实际工作中慢SQL治理的方法论
1、发现:如何发现慢SQL。
2、定位:如何定位到慢SQL写在哪。
3、分析:遇到慢SQL时的分析思路。
4、解决:慢SQL的解决思路。

SQL优化本身是一个比较复杂的问题,上面所列举的,只是部份优化的案例,但所有优化的思路都是尽可能的减少SQL在执行中过程中扫描数据块的次数,只要遵从这一核心思想,SQL优化并不是什么太难的事情。
以上就是我今天想谈的。本文是笔者在实际治理过程中的一些总结和体会。如有不合规之处,请予以纠正。

[En]

The above is what I want to talk about today. This article is some summary and experience of the author in the actual governance process. If there are any irregularities, please correct them.

Original: https://www.cnblogs.com/zhihong1/p/16018136.html
Author: 最初;
Title: 慢SQL治理方法论

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/505554/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL 主从同步延迟监控

    MySQL5.7和8.0支持通过 replication_applier_status 表获同步延迟时间,当从库出现延迟后,该表中的字段 REMAINING_DELAY 记录延迟秒…

    数据库 2023年6月11日
    0100
  • Etcd 使用场景:通过分布式锁思路实现自动选主

    分布式锁?选主? 分布式锁可以保证当有多台实例同时竞争一把锁时,只有一个人会成功,其他的都是失败。诸如共享资源修改、幂等、频控等场景都可以通过分布式锁来实现。 还有一种场景,也可以…

    数据库 2023年6月6日
    092
  • Consul 入门-gRPC 服务注册与发现

    前言 &#x5047;&#x5982;&#x6211;&#x6709;&#x94B1;&#xFF0C;&#x6211;&am…

    数据库 2023年6月6日
    094
  • 第五届蓝帽杯-溯源取证wp

    直接使用vmdk文件创建虚拟机,结果弹出提示 点击全部允许之后,进不去系统,到达了initramfs页面 由于提供的vmdk文件只有1G大小,将其转回raw文件,发现有10个G的大…

    数据库 2023年6月11日
    0105
  • jdbc分页

    一、最低级分页 resources下的db.properties db.driver=com.mysql.cj.jdbc.Driver db.url=jdbc:mysql://lo…

    数据库 2023年6月9日
    094
  • SQL中的排座位问题

    表: Seat +————-+———————+|…

    数据库 2023年5月24日
    065
  • Centos静默安装Oracle11G

    环境准备 Oracle 11gR2 64位 Linux版安装包 linux.x64_11gR2_database_1of2.ziplinux.x64_11gR2_database_…

    数据库 2023年6月16日
    069
  • [VSCode] Todo Tree VSCode插件 待办事项树

    Todo Tree 一款待办事项插件 我们写程序的时候,难免会遇到一些情况需要标记或者搁置,在写代码的时候会用一些特殊的注释来表示不同的内容,使我们可以快速的定位我们注释的位置。 …

    数据库 2023年6月9日
    083
  • JDBC概述

    JDBC概述 JDBC(Java DataBase Connectivity),它是一种用于执行 SQL语句的 JavaAPI。通过使用JDBC就可以使用 相同的API访问 不同的…

    数据库 2023年6月16日
    071
  • mysql对属性的增删改

    修改表 alter table 创建表db 查看表 desc与describe desc table 查看建表语句show create table t1; 修改表名 alter …

    数据库 2023年6月9日
    0104
  • 23种设计模式之责任链模式

    文章目录 概览 责任链模式的优缺点 责任链模式的结构和实现 * 模式的结构 模式的实现 总结 ; 概览 责任链模式(Chain of Responsibility Pattern)…

    数据库 2023年6月6日
    096
  • 【黄啊码】PHP对接阿里云短信服务最新版(官方文档有坑,切记)

    现在百度出来的大都是1.5的版本,而阿里云短信API出到了2.0了 以下是最新教程,绝对成功,不成功。。。我再改一遍! 环境要求最低要求 PHP 5.6安装 SDK 核心库 Ope…

    数据库 2023年6月16日
    0204
  • 从零开始的常用MySQL语句练习大全

    很多时候深入学习固然很重要,但是想要写下一篇给新手都能看得懂看的很香,并且老鸟可以查漏补缺的的练习博客,还是挺有难度,所以今天尝试写一些关于MySQL的语句练习大全,供想要从零开始…

    数据库 2023年6月11日
    083
  • 9.回文数

    给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 例如,121 是回文,而…

    数据库 2023年6月16日
    065
  • Jenkins+gitlab+docker+harbor容器化自动部署详细流程

    环境:Linux版本:Centos7 一、更新源:yum update 二、安装docker:yum install docker -y 启动docker: systemctl s…

    数据库 2023年6月16日
    0127
  • web监听器解析

    监听器是web三大组件之一,事件监听机制如下: 事件:某个事件,如果初始化上下文 事件源:事件发生的地方 监听器:一个对象,拥有需要执行的逻辑 注册监听:将事件、事件源、监听器绑定…

    数据库 2023年6月16日
    079
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球