MySQL 19 为什么我只查一行的语句,也执行这么慢?

有些情况下,"查一行"也会执行特别慢,今天就看看什么情况会出现这个现象。

如果MySQL本身有很大压力,导致数据库服务器CPU占有率很高或IO利用率很高,这种情况所有语句的执行都可能变慢,不在本文讨论范围内。

为了分析,构建有10万行记录的表,建表语句如下:

sql 复制代码
CREATE TABLE `t` ( 
    `id` int(11) NOT NULL, 
    `c` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

第一类:查询长时间不返回

比如执行语句:

sql 复制代码
select * from t where id=1;

查询结果长时间不返回:

一般碰到这种情况,大概率是表t被锁住。分析的时候,一般会先执行show processlist命令,查看当前语句处于什么状态,然后再针对每种状态分析它们产生的原因、如何复现,以及如何处理。

等MDL锁

使用show processlist命令示意图如下:

其中Waiting for table metadata lock状态表示,现在有一个线程正在表t上请求或者持有MDL写锁,将select语句堵住了。

比如有可能是如下的情况:

对于这种情况,可以通过查询sys.schema_table_lock_waits表,直接找出造成阻塞的process id,然后kill这个连接。

等flush

接下来看另外一种情况:

其中Waiting for table flush状态表示,现在有一个线程正要对表t做flush操作。MySQL里对表做flush操作的用法,一般有以下两个:

sql 复制代码
flush tables t with read lock;
flush tables with read lock;

如果指定表t,代表只关闭表t;如果没有指定具体的表名,则表示关闭MySQL里所有打开的表。

正常情况下,这两个语句执行起来都很快,除非它们也被别的线程堵住了。

所以出现Waiting for table flush状态的可能情况是:有一个flush tables命令被别的语句堵住了,然后它又堵住了select语句。可能的情况如下:

在session A中,每行都调用一次sleep(1),这样对于10万行的表,该语句默认执行10万秒,在这期间表t一直被session A"打开"着。然后,session B想要关闭表t就需要等session A查询结束,而session C会被flush命令堵住。

可以用show processlist查看process状态,然后手动kill相关process。

等行锁

由于访问id=1记录时要加读锁,如果这时候已经有一个事务在这行记录上持有一个写锁,select语句就会被堵住:

session A启动了事务,占有写锁还不提交,导致session B被堵住。

可以通过sys.innodb_lock_waits表查看是谁占着写锁:

sql 复制代码
select * from t sys.innodb_lock_waits where locked_table='`test`.`t`'\G

可以看到4号线程造成了堵塞,因此需要kill 4断开连接。当连接被断开,会自动回滚该连接里正在执行的流程,也就会释放id=1上的行锁。

第二类:查询慢

看一个只扫描一行,但执行很慢的语句:

sql 复制代码
select * from t where id=1;

其slow log如下:

可以发现,虽然扫描行数是1,但执行时间却长达800ms。

继续看slow log下面的内容,是下一个语句,扫描行数1行,执行时间为0.2ms:

看起来有些奇怪,毕竟lock in share mode还要加锁,按理说时间会更长。

查看这两个语句的执行输出结果:

第一个语句查询结果c=1,第二个语句查询结果c=1000001,所以里面有对c字段的改变,实际上对应下面这种情况:

session A先启动了事务,然后session B开始执行update语句。更新完成后,id=1对应的状态如下:

session B更新完,生成了100万个回滚日志。

第二个查询语句是当前读,会直接读到1000001这个结果,所以速度很快;而第一个查询语句是一致性读,需要回滚100万次得到1这个结果,因此速度很慢。

undo log里记录的其实是"把 2 改成 1","把 3 改成 2"这样的操作逻辑,画成减1是方便看图。

相关推荐
Fleshy数模5 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
az44yao6 小时前
mysql 创建事件 每天17点执行一个存储过程
mysql
秦老师Q7 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
橘子138 小时前
MySQL用户管理(十三)
数据库·mysql
Dxy12393102168 小时前
MySQL如何加唯一索引
android·数据库·mysql
我真的是大笨蛋8 小时前
深度解析InnoDB如何保障Buffer与磁盘数据一致性
java·数据库·sql·mysql·性能优化
怣508 小时前
MySQL数据检索入门:从零开始学SELECT查询
数据库·mysql
人道领域9 小时前
javaWeb从入门到进阶(SpringBoot事务管理及AOP)
java·数据库·mysql
千寻技术帮10 小时前
10404_基于Web的校园网络安全防御系统
网络·mysql·安全·web安全·springboot
spencer_tseng11 小时前
MySQL table backup
mysql