监控上收到了大量慢查的告警,业务也反馈查询很慢,随即打开电脑确认慢查的原因。
通过平台的慢查分析之后,我们发现慢查有以下特征:
通对慢查的大致分析,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操作,慢查消失。
MySQL InnoDB
支持MVCC
多版本,可以在普通的SELECT
时不加锁。利用多版本读取指定版本的行记录,降低加锁的次数,能极大提高数据库的并发读写能力。
Innodb
在事务的某个时刻记录下MySQL所有的活跃事务列表,保存到read view
里面。在之后的查询中,通过比较记录的事务ID和read view
里面的事务列表,判断记录是否可见。
在Innodb的行结构中,还存在三个系统列,分别是DATA_ROW_ID
、DATA_TRX_ID
、DATA_ROLL_PTR
。
DATA_ROW_ID
: 如果表没有显示定义主键,则采用MySQL自己生成的ROW_ID,为48-bit,否则表示的是用户自定义的主键值。DATA_TRX_ID
:表示这条记录的事务ID。如果是二级索引,只在page里面保存trx_id。DATA_ROLL_PTR
: 指向对应的回滚段的指针。read view
是在SQL语句执行之前申请的,其中RC隔离级别是每个SELECT都会申请,RR隔离级别的read view
是事务开始之后的第一个SQL申请,之后事务内的其他SQL都使用该read view
。
read view
中有三个变量需要重点关注:
low_limit_id
: 表示的是创建read view
那一刻活跃的事务列表的最大的事务IDup_limit_id
:表示的是创建read view
那一刻活跃的事务列表的最小的事务IDtrx_ids
:表示的创建read view
那一刻所有的活跃事务列表。DATA_TRX_ID
小于read vew
的up_limit_id
,说明该记录在创建read view
之前就已经提交,记录可见DATA_TRX_ID
和事务创建者的TRX_ID
一样,记录可见DATA_TRX_ID
大于read vew
的up_limit_id
,说明该记录在创建read view
之后进行的新建事务修改提交的,记录不可见read view
之前开始的,那么B事务里面的SQL是不能看到A事务执行的修改的。因此还有一条规则:如果记录对应的DATA_TRX_ID
在read view
的trx_ids
里面,那么该记录也是不可见的。UNDO日志是MVCC的重要组成部分,当一条数据被修改时,UNDO日志里面保存了记录的历史版本。当事务需要查询记录的历史版本时,可以通过UNDO日志构建特定版本的数据
每条行记录上面都有一个指针DATA_ROLL_PTR
,指向最近的UNDO记录。同时每条UNDO记录包含一个指向前一个UNDO记录的指针,这样就构成了一条记录的所有UNDO历史的链表。当UNDO的记录还存在,那么对应的记录的历史版本就能被构建出来。
当记录对应的版本通过DATA_TRX_ID
比对发现不可见时,通过系统列DATA_ROLL_PTR
,找到对应的回滚段记录,继续通过上述判断记录可见的规则,进行判断,如果记录依旧不可见,继续通过回滚段查找之前的版本,直到找到对应可见的版本。
经过和业务方沟通,得知该表每天都有定时任务,会删除历史数据。大致了解到整个过程后,我们搭建模拟环境进行测试。
测试分为三个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 limit 1
。
DATA_TRX_ID
,匹配可见规则1,记录可见由于被删除的记录有2000万,Innodb 需要扫描2000万的记录,才能找到符合条件的第一条记录,然后返回到MySQL的Server层。
当SQL为select * from sbtest1 order by id desc limit 1
。
由于删除的是老数据,当从ID最大的方向开始扫描时,通过MVCC 判断可见,然后判断记录是否被标记为删除的时候,记录没有被删除,因此就可以快速返回到Server层,SQL执行效率就会很高。
where c1=1
有10万match 的记录时,其中扫描方向的90%的记录都被标记为以及删除,但是还没purge,执行where c1 =1 limit 1
一样会慢。欢迎关注我们的公众号
Original url: Access
Created at: 2019-09-26 15:59:01
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论