墨墨导读:监控上收到了大量慢查的告警,业务也反馈查询很慢,本文记录整个慢查的原因,以及解决方案。

一、背景

监控上收到了大量慢查的告警,业务也反馈查询很慢,随即打开电脑确认慢查的原因。

二、现象描述

通过平台的慢查分析之后,我们发现慢查有以下特征:

  • 慢查的表名都是 sbtest1,没有其他的表;

  • 大部分的慢查都是查表最新的数据,例如 select * from sbtest1 limit 1;

  • rows examined 为 1,没有扫描大量的数据。

三、问题分析

通对慢查的大致分析,SQL 本身没有发现问题。那么是不是主机或者网络等有问题呢?

经过对网络和主机磁盘的 IO 等的分析,负载均正常,没有丢包的现象。

回到数据库本身,慢查还在,确认下慢查到底是慢在哪里。

当慢查在执行的时候,大部分的都是表现在 Sending data 的状态,我们通过 profiling 去确认下慢查的时间分布:

从图中,我们可以看到 sending data 耗费的时间为 0.945 秒,基本占据了 SQL 执行时间的99%。

那么 sending data 是什么意思呢,我们从官方文档里面了解下。

The thread is reading and processing rows for a SELECT statement, and sending data to the client. Because operations occurring during this state tend to perform large amounts of disk access (reads), it is often the longest-running state over the lifetime of a given query.

Sending data 表示在读取以及处理行数据以及发送数据到客户端,由于数据只有一行,且当时网络确认正常,那么时间就是耗费在读取和处理 select 的数据。

那为啥只取 limit 1,而且没有 where 条件的 SQL 执行扫描一行数据会这么慢呢?

打开监控,看看有没有啥指标异常。

我们注意到数据库的 History list length 这个指标一直在升高,达到了几万。慢查的执行时间是随着 History list length 升高而变的更慢。当 History list length 一直居高不下的时候,说明了有大量的 UNDO 没有被 purge。由于当前数据库的隔壁级别是 RR,开始比较早的事务,如果还没提交,就需要通过 UNDO 去构建对应版本历史时,保证数据库的可重复读(跟 MVCC 有关)。

既然 History list length 那么高,可能是有历史事务出现异常没有提交,也有可能是一致性快照的备份。可以通过 information_schema.innodb_trx 表去确认对应的事务信息。经过查询,的确发现一个事务执行了4个小时左右,没有提交,且不是备份用户。手动将该线程执行 kill 操作,慢查消失。

3.1 聊一下 MVCC

MySQL InnoDB 支持 MVCC 多版本,可以在普通的 SELECT 时不加锁。利用多版本读取指定版本的行记录,降低加锁的次数,能极大提高数据库的并发读写能力。

Innodb 在事务的某个时刻记录下 MySQL 所有的活跃事务列表,保存到 read view 里面。在之后的查询中,通过比较记录的事务ID和 read view 里面的事务列表,判断记录是否可见。

3.1.1 Innodb 行记录

在 Innodb 的行结构中,还存在三个系统列,分别是 DATA_ROW_IDDATA_TRX_IDDATA_ROLL_PTR

  • DATA_ROW_ID: 如果表没有显示定义主键,则采用 MySQL 自己生成的 ROW_ID,为 48-bit,否则表示的是用户自定义的主键值;

  • DATA_TRX_ID:表示这条记录的事务 ID。如果是二级索引,只在 page 里面保存 trx_id;

  • DATA_ROLL_PTR: 指向对应的回滚段的指针。

3.1.2 read view

read view 是在 SQL 语句执行之前申请的,其中 RC 隔离级别是每个 SELECT 都会申请,RR 隔离级别的 read view 是事务开始之后的第一个 SQL 申请,之后事务内的其他 SQL 都使用该 read view

read view 中有三个变量需要重点关注:

  • low_limit_id:表示的是创建 read view 那一刻活跃的事务列表的最大的事务 ID;

  • up_limit_id:表示的是创建 read view 那一刻活跃的事务列表的最小的事务 ID;

  • trx_ids:表示的创建 read view 那一刻所有的活跃事务列表。

3.1.3 判断记录可见

  • 当记录的 DATA_TRX_ID 小于 read vew 的 up_limit_id,说明该记录在创建 read view 之前就已经提交,记录可见;

  • 如果记录的 DATA_TRX_ID 和事务创建者的 TRX_ID 一样,记录可见;

  • 当记录的 DATA_TRX_ID 大于 read view 的 up_limit_id,说明该记录在创建 read view 之后进行的新建事务修改提交的,记录不可见;

  • 在 RR 隔离级别,如果 A 事务在 B 事务创建 read view 之前开始的,那么 B 事务里面的 SQL 是不能看到 A 事务执行的修改的。因此还有一条规则:如果记录对应的 DATA_TRX_ID 在 read view 的 trx_ids 里面,那么该记录也是不可见的。

3.1.4 DATA_ROLL_PTR

UNDO 日志是 MVCC 的重要组成部分,当一条数据被修改时,UNDO 日志里面保存了记录的历史版本。当事务需要查询记录的历史版本时,可以通过 UNDO 日志构建特定版本的数据。

每条行记录上面都有一个指针 DATA_ROLL_PTR,指向最近的 UNDO 记录。同时每条 UNDO 记录包含一个指向前一个 UNDO 记录的指针,这样就构成了一条记录的所有 UNDO 历史的链表。当 UNDO 的记录还存在,那么对应的记录的历史版本就能被构建出来。

当记录对应的版本通过 DATA_TRX_ID 比对发现不可见时,通过系统列 DATA_ROLL_PTR,找到对应的回滚段记录,继续通过上述判断记录可见的规则,进行判断,如果记录依旧不可见,继续通过回滚段查找之前的版本,直到找到对应可见的版本。

3.2 慢查问题复现

经过和业务方沟通,得知该表每天都有定时任务,会删除历史数据。大致了解到整个过程后,我们搭建模拟环境进行测试。

测试分为三个 session,其中 Sess1 执行长事务,没有提交。Sess2 对表的历史数据做清理,清理了 2000 万的数据。此时在 Sess3 执行查询,快慢情况如上图所示。select * from sbtest1 limit 1 跟预期表现一样,为很慢。但是 select * from sbtest1 order by id desc limit 1 执行很快,这是为什么呢?

上图为主键的记录格式,在每条主键记录的前面有个删除标志位,然后是主键 ID,事务 ID,回滚段指针,最后是行记录。

当记录被执行删除的时候,MySQL 只是将记录标记为已删除,同时更新 DATA_TRX_ID 为自己删除会话的事务 ID,并没有将记录真正删除。当被删除的记录不再被其他事务需要的时候,会被 purge 线程删除。purge 线程负责清理这些真正被删除的记录以及不再需要的 UNDO 日志。

回到慢查本身,我们来看看慢查的执行过程。

SQL 为 select * from sbtest1 limit1

  • 通过主键,扫描到 ID=1 的记录,根据 MVCC 比对,发现自己的事务 ID 大于记录的 DATA_TRX_ID,匹配可见规则 1,记录可见;

  • 由于 ID=1 已经被标记为 DELETED,删除记录可见;

  • 由于表数据还没有全部扫描完成,未满足 limit 1,继续扫描下一条记录;

  • 扫描到 ID=2 的记录,根据 MVCC 比对,发现自己的事务 ID 大于记录的 DATA_TRX_ID,匹配可见规则 1,记录可见;

  • 由于 ID=2 已经被标记为 DELETED,删除记录可见;

  • 由于表数据还没有全部扫描完成,未满足 limit 1,继续扫描下一条记录;

  • 重复 4-6 步骤,直到满足找到一条记录,或者全表扫描完成。

由于被删除的记录有 2000 万,Innodb 需要扫描 2000 万的记录,才能找到符合条件的第一条记录,然后返回到 MySQL 的 Server 层。

当 SQL 为 select * from sbtest1 order by id desc limit1

由于删除的是老数据,当从 ID 最大的方向开始扫描时,通过 MVCC 判断可见,然后判断记录是否被标记为删除的时候,记录没有被删除,因此就可以快速返回到 Server 层,SQL 执行效率就会很高。

四、总结

  • 当新的会话执行 SQL 的时候,如果扫描区间范围内有大量被标记为删除的记录,会导致 SQL 执行效率变低,通过二级索引去查询也是类似的。例如当 where c1=1 有 10 万 match 的记录时,其中扫描方向的 90% 的记录都被标记为以及删除,但是还没 purge,执行 where c1=1 limit 1 一样会慢;

  • 如果一个会话没有开启自动提交,那么查到的记录就是会话开始的时候的第一个 SQL 执行的快照。如果查询的行记录的历史版本过多,将导致通过 UNDO 去构建历史版本的时间过长,对于高负载的 OLTP 系统来说是个灾难;

  • 尽可能的避免长事务,监控长事务的个数,进行告警,及时kill会话;

  • 已经提交的事务的 UNDO 只有在隔离级别是RR的情况下才会被比自己早的事务使用,如果隔离级别是 RC,提交的事务的 UNDO 会很快被 purge,就不会有本文上面出现的问题。


Q&A


Q1:慢的原因是遍历很多数据,因为多数判断为删除了吗?和History那个指标高没有直接关系,指标高只是一个表现而已。

A1:是的,History高还会导致有些SQL在构建一致快照的时候大量遍历UNDO而变慢,本案例的SQL是因为要遍历很多delete marked 的数据变慢的。

Q2:想咨询下,文中写的这种场景,有删除大量数据,并且也有查询的话,该怎么处理呢?
A2:查询可以采用小事务,避免长时间的查询,及时终止长时间的异常事务。

出处:有赞coder(ID:youzan_coder)

扩展阅读


  1. 【MySQL】磁盘写满之后,数据库show status受到阻塞的原因

  2. 不用Oracle?基于MySQL数据库下亿级数据的分库分表

  3. 史上最详细MySQL全局锁和表锁

  4. MySQL数据库备份之主从同步配置

  5. MySQL高可用实现:主从结构下ProxySQL中的读写分离

公司简介  | 招聘 | DTCC | 数据技术嘉年华 | 免费课程 | 入驻华为严选商城

zCloud | SQM | Bethune Pro2 | zData一体机 | MyData一体机 | ZDBM 备份一体机

Oracle技术架构 | 免费课程 | 数据库排行榜 | DBASK问题集萃 | 技术通讯

升级迁移 | 性能优化 | 智能整合 | 安全保障 |  架构设计 | SQL审核 | 分布式架构 | 高可用容灾 | 运维代维

云和恩墨大讲堂 | 一个分享交流的地方

长按,识别二维码,加入万人交流社群

请备注:云和恩墨大讲堂

详述一次大量删除导致MySQL慢查的分析相关推荐

  1. 详述一次大量删除导致MySQL慢查的过程

    墨墨导读:本文记录一次大量删除导致MySQL慢查的分析,大家有没有遇到过这种问题? 今晚20:30-21:30,相约云和恩墨大讲堂,从cost=0异常分析看Oracle统计信息的奥秘,详情及报名请戳: ...

  2. 数据库-优化-mysql慢查日志分析工具-mysqldumpslow介绍及用法

    MySQL慢查日志分析工具(mysqldumpslow) 1.介绍 如何进行查看慢查询日志,如果开启了慢查询日志,就会生成很多的数据,然后我们就可以通过对日志的分析,生成分析报表,然后通过报表进行优化 ...

  3. mysql索引失效_导致MySQL索引失效的几种常见写法

    最近一直忙着处理原来老项目遗留的一些SQL优化问题,由于当初表的设计以及字段设计的问题,随着业务的增长,出现了大量的慢SQL,导致MySQL的CPU资源飙升,基于此,给大家简单分享下这些比较使用的易于 ...

  4. like左匹配索引失效_导致MySQL索引失效的一些常见写法总结

    前言 最近一直忙着处理原来老项目遗留的一些SQL优化问题,由于当初表的设计以及字段设计的问题,随着业务的增长,出现了大量的慢SQL,导致MySQL的CPU资源飙升,基于此,给大家简单分享下这些比较使用 ...

  5. mysql ddl 锁_MySQL Online DDL导致全局锁表案例分析

    MySQL Online DDL导致全局锁表案例分析 我这边遇到了什么问题? 线上给某个表执行新增索引SQL, 然后整个数据CPU打到100%, 连接数暴增到极限, 最后导致所有访问数据库的应用都奔溃 ...

  6. ip变更 mysql无法启动_ubuntu mysql 更改IP导致mysql无法启动

    bind-address = 127.0.0.1 => bind-address= 136.129.20.168 IP要这么改 这么改远程连不上,那么需要把这行整行注释掉,重启MYSQL,tel ...

  7. 非法关机 mysql_一次非法关机导致mysql数据表损坏的实例解决 -电脑资料

    排查修复数据表的经过: 2.启动mysql服务,卸载和关闭rpm安装的mysql服务 (昨天安装postfix好像yum安装了mysql),用netstat -anp |grep mysqld 命令查 ...

  8. mysql批量insert bug_MySQL Bug insert into on duplicate key update 语法更新 text blob 大字段导致 MySQL crash...

    1. 背景 业务执行 SQL 导致 MySQL 进程 Crash,做故障切换后,新的主库又 Crash 了.查看 MySQL 错误日志,发现多次 Crash 时的堆栈相同,如下: Thread poi ...

  9. 怎么操作会导致MySQL锁表

    怎么操作会导致MySQL锁表 转载于:https://www.cnblogs.com/luao/p/10867785.html

最新文章

  1. coding怎么上传项目 mac_临近毕业,做过的项目怎么处理?
  2. python找出主力合约价格_如何找出全部期货主力合约和次主力合约?
  3. Jquery 日历控件
  4. 通过Flume简单实现Kafka与Hive对接(Json格式)
  5. 关于C语言野指针的问题
  6. 服务器是怎么工作的?(二)——arp工作原理详细解析
  7. golang 之 import 和 package 的使用
  8. 网络安全系列之四十 在Linux中设置SET位权限
  9. 海量数据挖掘之中移动流量运营系统
  10. 关于Windows 10驱动无法使用,无法验证设备所需的驱动程序的数字签名问题
  11. 【python】文件读取写 open的方式with的方式 异常报错处理
  12. android游戏基址,Android上可执行ELF文件中的段不能有基址
  13. 都2022年了相册分类还如此不准?不如试试亲影
  14. Linux系统VPS云服务器硬件/网速/性能/延迟测试一键脚本分享
  15. CDA数据分析师视频教程全套零基础入门excel考试教学课程2022
  16. 基于微信小程序公交查询系统设计与实现
  17. 利用github和godaddy搭建使用二级域名的个人主页
  18. bat递归查找指定文件_教你在电脑上用批处理脚本全盘查找文件
  19. MindMaster思维导图国内版更名“亿图脑图MindMaster”国产软件加速崛起!
  20. Shell脚本语法小全

热门文章

  1. rio indy_RIO Journal是否会成为同类中最开放的?
  2. 调优 | 别再说你不会 JVM 性能监控和调优了
  3. 面试官 | Java转List三种方式,你说说吧。我。。懵逼。啥时候有三种了
  4. 十进制负数的二进制表示法
  5. Bootstrap Well 组件
  6. Bootstrap3 表格的情景类
  7. Bootstrap 禁用某个菜单项
  8. ROS笔记(39) 串口配置
  9. 高级软件工程第七次作业:东理三剑客团队作业-随笔2
  10. vue中页面跳转,显示在顶部