MySQL 笔记整理(19) --为什么我只查一行的语句,也执行这么慢?
笔记记录自林晓斌(丁奇)老师的《MySQL实战45讲》
(本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除)
19) --为什么我只查一行的语句,也执行这么慢?
需要说明一下,如果MySQL数据库本身就有很大的压力,导致数据库服务器CPU占用率很高或ioutil(IO利用率)很高,这种情况下所有的语句执行都有可能变慢,不属于我们今天的讨论范围。为了便于描述,我们构造一个表,基于这个表来说明问题。这个表有两个字段id和c,并且我们事先插入了10万行记录。
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB; delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into t values(i,i);
set i=i+1;
end while;
end;;
delimiter ; call idata();
第一类:查询长时间不返回
执行如下语句 select * from t where id = 1;结果很长时间不返回结果,这是为什么呢?一般这种情况,大概率是表t被锁住了。这种时候我们就需要show processlist命令来查看当前语句处于什么状态了。然后根据每种状态,分析原因,如何复现以及如何处理。
等MDL锁:
当我们使用show processlist命令时如果看到了 Waiting for table metadata lock的结果,就表示有一个线程正在表t上请求或者持有MDL写锁,把select语句堵住了。我们可以这么来复现:
Session A通过lock table命令持有表t的MDL写锁,而session B的查询需要获取MDL读锁。所以,session B进入等待状态。而这类问题的处理方式,就是找到持有MDL写锁的家伙,然后把它kill掉。但是,由于在show processlist的结果里面,session A的Command列是“Sleep”,导致查找起来不方便。不过有了performance_schema和sys系统库以后,就方便多了。(MySQL启动时需要设置performance_schema=on,相比于设置为off,会有10%的性能损失)。通过查询sys.schem_table_lock_waits这张表,我们就可以直接找出造成阻塞的process id,把这个连接用kill命令断开即可。
等flush
如果我们的show processlist看到了"Waiting for table flush",说明现在有一个线程正要对表t做flush操作。MySQL里面对表做flush操作的用法,一般有下面两个:
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),这样表t一直是被session"打开",然后,session B的flush tables命令要去关闭表t,就需要等待session A的查询结束。这样,session C要再次查询的话,就会被flush命令堵住了。这个例子的处理方式很前面的一样,就不再赘述了。
等行锁:
经过上面的考验,我们的select语句终于来到了引擎里
mysql> select *from t where id - 1 lock in share model;
由于访问id=1这个记录时要加读锁,如果这个时候已经有一个事务在这行记录上持有一个写锁,我们的select语句就会被堵住。复现步骤如下:
这时如果你使用show processlist命令会发现statistics。我们来看复现步骤,sessionA启动了事务,占用写锁,还不提交,是导致sessionB被堵住的原因。这个问题不难分析,但问题是怎么查出是谁占用了这个写锁,如果你的MySQL5.7或以上版本,你可以通过sys.innodb_lock_waits表查到,查询命令是:
mysql> select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
但这里有一个需要注意的地方,当你查出罪魁祸首,比如线程4,你不能直接使用kill query 4;这个命令表示停止4号线程当前正在执行的语句,而这个方法其实是没有用的。因为占有行锁的是update语句,这个语句已经是之前执行完成了的,现在执行kill query,无法让这个事务去掉id=1的行锁。实际上,kill 4 才有效。也就是说直接断开这个连接。这里隐含的一个逻辑是,连接被断开的时候,会自动回滚这个连接里面正在执行的线程,也就释放了id=1的行锁。
第二类:查询慢
我们先来看一条sql语句
mysql>select * from t where c=50000 limit 1;
由于字段c上没有索引,这个语句只能走id主键顺序扫描,因此需要扫描50000行。你可以先在慢查询日志里确认一下。为了确保被慢查询日志记录下来,你可以先执行set long_query_time=0。将慢查询日志的时间阈值设置为0.虽然实际上你可能会看到返回结果不是很慢,但有一个原则是这样:坏查询不一定是慢查询。我们这个例子里面只有10万行记录,数据量大起来的话,执行时间就会线性涨上去了。
扫描行数多,执行慢,这个很好理解。我们再来看一个只扫描了一行,但是执行很慢的语句。执行的语句是这样的。
mysql>select * from t where id=1;
它的查询日志是这样的:
还是这个查询,如果我们再加上 lock in share model,执行时间居然会变快:
这是为什么呢?按理来说,加上了锁不应该更慢吗?我们再来看一个提示信息,下图是这两个语句的执行输出结果:
我们的第一个查询返回的结果是c=1,而带lock in share mode的语句返回的是c=1000001。如果你还是没有头绪,也别着急,我们来看看复现步骤:
你看到了,session A先用start transaction with consistent snapshot命令启动了一个事务,之后sessionB才开始执行update语句。SessionB执行完100万次update语句后,id=1这一行处于什么状态呢?你可以从下图找到答案:
session B 更新完成100万次,生成了100万个回滚日志(undo log).带lock in share mode的SQL语句,是当前读,因此会直接读到10000001这个结果,所以速度很快;而select * from t where id = 1这个语句,是一致性读,因此需要从1000001开始,依次执行updo log,执行了100万次一行,才将结果1返回。
注意,undo log里面的记录其实是“把2改成1”这样,而不是图中的“-1”;
MySQL 笔记整理(19) --为什么我只查一行的语句,也执行这么慢?的更多相关文章
- 最全mysql笔记整理
mysql笔记整理 作者:python技术人 博客:https://www.cnblogs.com/lpdeboke Windows服务 -- 启动MySQL net start mysql -- 创 ...
- 《Mysql - 为什么只查一行的数据,也这么慢?》
概念 - 在某些情况下,“查一行”,也会执行得特别慢. - 下面分析在什么情况下,会出现这个现象. - 基础工作(构建数据库环境) - 建立 t 表,并写入 10W 的数据. CREATE TABLE ...
- MySQL 笔记整理(16) --“order by”是怎么工作的?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 16) --“order by”是怎么工作的? 在林老师的课程中,第15 ...
- MySQL 笔记整理(5) --深入浅出索引(下)
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 5) --深入浅出索引(下) 这次的笔记从一个简单的查询开始: 建表语句是这样的 mysql> create table T ...
- MySQL 笔记整理(17) --如何正确地显示随机消息?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 17) --如何正确地显示随机消息? 如果有这么一个英语单词表,需要每次 ...
- MySQL 笔记整理(9) --普通索引和唯一索引,应该怎么选择?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 9) --普通索引和唯一索引,应该怎么选择? 假如你在维护一个市民系统, ...
- MySQL 笔记整理(1) --基础架构,一条SQL查询语句如何执行
最近在学习林晓斌(丁奇)老师的<MySQL实战45讲>,受益匪浅,做一些笔记整理一下,帮助学习.如果有小伙伴感兴趣的话推荐原版课程,很不错. 1) --基础架构,一条SQL查询语句如何执行 ...
- MySQL 笔记整理(20) --幻读是什么,幻读有什么问题?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 20) --幻读是什么,幻读有什么问题? 我们先来看看表结构和初始化数据 ...
- MySQL 笔记整理(2) --日志系统,一条SQL查询语句如何执行
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 2) --日志系统,一条SQL查询语句如何执行 MySQL可以恢复到半个月内任意一秒的状态,它的实现和日志系统有关.上一篇中记录了一 ...
随机推荐
- COGS2421 [HZOI 2016]简单的Treap
题面见这里 大概是个模板题 Treap暴力插入的做法太暴力了并不优美 这里就需要用到笛卡尔树的构造方法,定义见这里 在 假的O(n) 的时间内构造一棵Treap 把元素从小到大排序 这样从小到大插入时 ...
- Jenkins 集成 SonarQube Scanner
1. 安装Jenkins 下载安装包,这里我们下载war包 https://jenkins.io/download/ 运行jenkins.war的方式有两种: 第一种:将其放到tomcat中运行( ...
- asp.net core系列 55 IS4结合Identity密码保护API
一.概述 OAuth 2.资源所有者密码授权允许客户端(Client项目)向令牌服务(IdentityServer项目)发送用户名和密码,并获取代表该用户的访问令牌.本篇将IS4结合asp.net c ...
- java游戏开发杂谈 - 实现游戏主菜单
经常玩游戏的同学,大家都知道,游戏都会有个主菜单,里面有多个菜单选项:开始游戏.游戏设置.关于游戏.退出游戏等等,这个菜单是怎么实现的呢. 有一定桌面软件开发基础的同学可能会想到,用JButton组件 ...
- Android 8.0对隐式广播的进一步限制
项目targetSdkVersion升级到26后,对应的的是Android O版本,即Android 8.0系统.经测试发现针对8.0及以上安卓版本手机,AndroidMainfest.xml中静态注 ...
- Spring boot打包war包
1.设置打包的类型(war/jar) 在pom.xml里设置 <packaging>war</packaging> 2.移除嵌入式tomcat插件 //在pom.xml里找到s ...
- 激活效能,CODING 敏捷研发模块上线
昨晚,巴黎圣母院失火,而我们当中的许多人都还没来得及去欣赏它的真容.我们曾以为美好的事物会等待我们,伟大的目标也会等待我们.世事无常,唯一不变的就是变化.在软件研发领域,敏捷研发就是这么一个小步快跑来 ...
- weblogic816 bug list
weblogic816在aix下的补丁汇总: 严重 Patch 8173326,weblogic server挂起,threaddump显示SERVER HANGS TRYING TO CALL LO ...
- C#中关于表达式与委托在EF中的不同表现总结
Func<Invoice, bool> func = x => x.State == InvoiceState.Created; Expression<Func<Invo ...
- Lnmp一键脚本
#!/bin/bash #================================================================ # Copyright (C) 2018 A ...