是谁,把InnoDB表上的DML搞慢的?

0、导读

突然发现MySQL服务器上InnoDB表的DML线程频繁被阻塞,TPS下降比较厉害,是什么原因导致?

1、问题

我的朋友小明(又是悲催的小明),发现有个MySQL数据库最近DML明显变慢了,执行SHOW PROCESSLIST总能看到DML线程状态,TPS下降也挺厉害的,不知道什么原因。

接到小明的求助,我第一反应是,可能有几个原因:

  1. 服务器的性能不足;
  2. InnoDB buffer pool size分配不够;
  3. 表DDL设计不合理,例如没有基于索引的SQL请求,或者InnoDB表没有使用自增列做主键,或索引太多;
  4. MySQL中有些SQL效率太低,影响整体性能;
  5. 当前有其他进程负载较大,影响到mysqld进程的性能;

那么到底是什么原因导致InnoDB表上的DML操作很慢呢,下面一起来一步步排查吧。

2、排查

2.1、查看整体负载

先执行top看系统负载,看看高不高,并确认瓶颈是否在mysqld进程:

可以看到,系统负载不算高,mysqld进程的CPU消耗比较大,疑似索引可能存在问题。

从top的结果也没看到有其他进程在和mysqld进程抢占资源,消耗cpu或内存等资源。

另外mysqld进程占用的内存不小,按理不应该是InnoDB buffer pool分配不足的原因了。不过,我们也注意到used很大,但buffers+cached不大,疑似发生内存泄露。

2.1、查看MySQL内部SQL请求

接下来我们看看MySQL内部的一些SQL请求,是否存在索引设计不当导致SQL效率太差的情况。

并没有耗时特别久的SELECT请求,排除这个因素。

同时也排除了因为表DDL设计不合理导致DML太慢的因素,这个的背景知识详见[MySQL FAQ]系列 — 为什么InnoDB表要建议用自增列做主键

排查到这里,似乎无解了?

等等,我们再来检查InnoDB status看看:

从上面这些信息里我们注意到几个重要信息:

  • un-Purge太大了,足有46万(History list length 462312,太大了);
  • 个别事务启动太久一直没提交(TRANSACTION 8682844862, ACTIVE 3235 sec,将近1个小时);
  • 个别事务里生成的undo太多(undo log entries 14365,一个事务里1万多undo,要死人的节奏);
  • buffer pool中的脏页不算多(Modified db pages  134050,约2GB,相对于总共90G的buffer pool才占2.2%,也还好);

基本可以怀疑是因为unpurge list太大,导致DML被阻塞了,看看InnoDB的几个选项确认下:

上面这2个配置选项的意思是:

1、当InnoDB检测到当前unpurge list大于10万时,就会阻塞所有的DML请求;

2、每次DML请求被阻塞延时至少是  ((purge_lag/innodb_max_purge_lag)×10)–5 毫秒,最大阻塞延时是1000毫秒(1秒)。在上面的例子中,最少被阻塞 (462312/430000)*10-5=5.7514 毫秒,最久1秒;

这就能很好解释为什么InnoDB表上的DML请求总是那么慢了。

但是,为什么unpurge会这么大呢,继续追根刨底。

我们先来看下 information_schema.innodb_trx 视图,这个视图记录了当前所有活跃事务列表:

共有3个活跃事务,其中最早的一个是04:39:02开始的,而当前时间是05:32:25,两者相差了将近1个小时。可见,是因为有个较早的事务,一直没有提交/回滚,导致这个unpurge的列表越来越大在InnoDB中,执行purge工作前要先判断哪些数据(主要是Undo Log,被标记为删除的数据,以及二级索引中被删除的记录)可以被删除

在本例中,因为有个事务长时间没提交,它有可能需要读取这些旧数据,所以是不能被purge的,直到确认没有任何事务需要读取这些旧数据时,才能将它们清除。

3、解决方案

至此,我们知道为啥DML会很慢的原因了,解决起来就简单了:

  • 及时提交长时间的事务,例如本例中应该及时提交线程ID为4886791的事务,实在找不到是哪个业务连接的话,就只能直接把这个线程给kill了。我在最近的几次大会上分享时,也提到应该及时杀掉长时间不活跃的连接,因为它们是有可能存在未提交的事务的。这个是治本的办法;
  • 设置autocommit=1,尤其应该检查一些gui客户端,例如MySQL Workbench、Navicat,它们极有可能在连接MySQL时采用autocommit=0的模式,应该调整过来。这个也算是治本的办法之一;
  • 在避免undo log暴涨的前提下,可以适当调大 innodb_max_purge_lag 值以允许存在更大的unpurge列表,还应适当调低 innodb_max_purge_lag_delay 值,降低每次DML被阻塞的耗时。这个是治标的办法,不太建议;

文章转自老叶茶馆公众号,原文链接:https://mp.weixin.qq.com/s/d5jbmdu1Yc5CG1jgIkTUCQ

时间: 2024-08-03 08:49:09

是谁,把InnoDB表上的DML搞慢的?的相关文章

InnoDB 中文参考手册 --- 4 建立 InnoDB 表

参考|参考手册|中文 4 建立 InnoDB 表假设你已以 mysql test 命令方式运行了 MySQL 客户端程序.为了建立一个 InnoDB 格式的表你必须在 SQL 命令中指定 TYPE = InnoDB : CREATE TABLE CUSTOMER (A INT, B CHAR (20), INDEX (A)) TYPE = InnoDB; 这个 SQL 命令将在 my.cnf 中设定的 InnoDB 数据文件中创建一个表和一个列 A 的索引,同时将在 MySQL 数据目录下的 t

mysql中innodb表中count()优化

起因:在innodb表上做count(*)统计实在是太慢了,因此想办法看能不能再快点. 现象:先来看几个测试案例,如下 一. sbtest 表上的测试 show create table sbtest\G *************************** 1. row *************************** Table: sbtest Create Table: CREATE TABLE `sbtest` ( `aid` bigint(20) unsigned NOT NU

RDS for MySQL InnoDB 表级锁等待

RDS for MySQL InnoDB 表级锁等待   1. 显式 lock table 2. 隐式 lock table 在 RDS MySQL 实例日常使用中,有些情况下会发现出现 InnoDB 表级锁等待的情况,下面列出常见的2个原因.  1. 显式 lock table 执行了 lock tables tab_name read; 导致 DML 会话等待在表的表级锁上. 会话 1 lock tables tab_name read; 会话 2 会话 3   2. 隐式 lock tab

InnoDB 中文参考手册 --- 14 InnoDB 表的限制

参考|参考手册|中文 InnoDB 中文参考手册 --- 犬犬(心帆)翻译 14 InnoDB 表的限制 在 < 3.23.50 版本的 InnoDB 中,不可以使用 ALTER TABLE 或 CREATE INDEX 来修改一个已经有了外键约束或参考了外键约束的表.使用 DROP TABLE 和 CREATE TABLE 来代替它. 不可以将 MySQL 系统表(如 'user' 或 'host' )转换成 InnoDB 类型.系统表必须总是为 MyISAM 类型的. InnoDB 表不支持

MySQL InnoDB表空间及日志文件简介

MySQL一个显著的特点是其可插拔的存储引擎,因此MySQL文件分为两种:一种是MySQL服务器本身的文件(主要是一 些日志文件,如错误日志.二进制日志等),所有的存储引擎共享:另一种是和具体存储引擎相关的文件.本文主要介 绍和InnoDB存储引擎相关的文件(数据+日志),至于MySQL服务器本身的日志文件,可以参考<[MySQL] 日志文件概述 >. InnoDB表空间文件 InnoDB在很多方面和Oracle非常像,它的数据也是按表空间存储的,表空间是一个在逻辑上为整体的存储块,默认情 况

MySQL数据库INNODB 表损坏修复过程

  突然收到MySQL报警,从库的数据库挂了,一直在不停的重启,打开错误日志,发现有张表坏了.innodb表损坏不能通过repair table 等修复myisam的命令操作.现在记录下解决过程,下次遇到就不会这么手忙脚乱了. 处理过程: 一遇到报警之后,直接打开错误日志,里面的信息: InnoDB: Database page corruption on disk or a failed InnoDB: file read of page 30506. InnoDB: You may have

InnoDB表快速修改字段名方案

最近被问到一个问题,InnoDB表,只修改一个字段的名字,定义不修改,是否有快速方案. 这个需求的意义来源于,在表设计初期可以预留一些字段,但在预留字段投入使用时,最好能够赋予一个有意义的名字以方便使用. 复现 以下实验基于5.1.48版本. 创建一个简单表 CREATE TABLE `t` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk; 插入10w条数据.将字

MySQL 备份恢复单个innodb表的教程

在实际环境中,时不时需要备份恢复单个或多个表(注意:这里除非明确指定,所说的表一律指InnoDB表),而对于innodb引擎恢复单个表需要整体的恢复,xtrabackup也可以单个表恢复,只不过是用的正则过滤的,不知最新版本是否支持表空间传输特性.本文将要说说怎么移动或复制部分或全部的表到另一台服务器上,而所要用到的技术点就是transportable tablespace特性,这就意味着MySQL5.6.6以及以上版本才支持. 表空间传输特性允许表空间从一个实例移动到另一个实例上.这在以前版本

提高MySQL中InnoDB表BLOB列的存储效率的教程_Mysql

首先,介绍下关于InnoDB引擎存储格式的几个要点: 1.InnoDB可以选择使用共享表空间或者是独立表空间方式,建议使用独立表空间,便于管理.维护.启用 innodb_file_per_table 选项,5.5以后可以在线动态修改生效,并且执行 ALTER TABLE xx ENGINE = InnoDB 将现有表转成独立表空间,早于5.5的版本,修改完这个选项后,需要重启才能生效: 2.InnoDB的data page默认16KB,5.6版本以后,新增选项 innodb_page_size