笔记记录自林晓斌(丁奇)老师的《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) --为什么我只查一行的语句,也执行这么慢?的更多相关文章

  1. 最全mysql笔记整理

    mysql笔记整理 作者:python技术人 博客:https://www.cnblogs.com/lpdeboke Windows服务 -- 启动MySQL net start mysql -- 创 ...

  2. 《Mysql - 为什么只查一行的数据,也这么慢?》

    概念 - 在某些情况下,“查一行”,也会执行得特别慢. - 下面分析在什么情况下,会出现这个现象. - 基础工作(构建数据库环境) - 建立 t 表,并写入 10W 的数据. CREATE TABLE ...

  3. MySQL 笔记整理(16) --“order by”是怎么工作的?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 16) --“order by”是怎么工作的? 在林老师的课程中,第15 ...

  4. MySQL 笔记整理(5) --深入浅出索引(下)

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 5) --深入浅出索引(下) 这次的笔记从一个简单的查询开始: 建表语句是这样的 mysql> create table T ...

  5. MySQL 笔记整理(17) --如何正确地显示随机消息?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 17) --如何正确地显示随机消息? 如果有这么一个英语单词表,需要每次 ...

  6. MySQL 笔记整理(9) --普通索引和唯一索引,应该怎么选择?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 9) --普通索引和唯一索引,应该怎么选择? 假如你在维护一个市民系统, ...

  7. MySQL 笔记整理(1) --基础架构,一条SQL查询语句如何执行

    最近在学习林晓斌(丁奇)老师的<MySQL实战45讲>,受益匪浅,做一些笔记整理一下,帮助学习.如果有小伙伴感兴趣的话推荐原版课程,很不错. 1) --基础架构,一条SQL查询语句如何执行 ...

  8. MySQL 笔记整理(20) --幻读是什么,幻读有什么问题?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 20) --幻读是什么,幻读有什么问题? 我们先来看看表结构和初始化数据 ...

  9. MySQL 笔记整理(2) --日志系统,一条SQL查询语句如何执行

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 2) --日志系统,一条SQL查询语句如何执行 MySQL可以恢复到半个月内任意一秒的状态,它的实现和日志系统有关.上一篇中记录了一 ...

随机推荐

  1. Python3 ——斐波那契数列(经典)

    刚刚学习了 斐波那契数列,整理一下思路,写个博文给未来的学弟学妹参考一下,希望能够帮助到他们 永远爱你们的 ----新宝宝 经历过简单的学习之后,写出一个比较简单的代码,斐波那契数列:具体程序如下: ...

  2. 轻松搞定JSONP跨域请求

    一.同源策略 要理解跨域,先要了解一下"同源策略".所谓同源是指,域名,协议,端口相同.所谓"同源策略",简单的说就是基于安全考虑,当前域不能访问其他域的东西. ...

  3. 记一次产品需求:图片等比缩放和CSS自适应布局16:9

    前言 前阵子,产品跑过来问我现有的模板中没有图片模板,需要添加一个图片模板:然而,他要求图片在展示区最好能够实现随着窗口的变化而自动按图片比例等比缩放,并且居中展示图片.我当时想着,抛开技术实现层面, ...

  4. 深度学习与自动驾驶领域的数据集(KITTI,Oxford,Cityscape,Comma.ai,BDDV,TORCS,Udacity,GTA,CARLA,Carcraft)

    http://blog.csdn.net/solomon1558/article/details/70173223 Torontocity HCI middlebury caltech 行人检测数据集 ...

  5. 【STM32H7教程】第4章 STM32H7工程模板建立(MDK5)

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第4章   STM32H7工程模板建立(MDK5) 本章 ...

  6. [转载] Java中枚举类型的使用 - enum

    目录 1 枚举类的编译特性 2 向枚举类中添加方法 3 接口内部创建枚举 4 枚举类中使用枚举 5 扩展: 验证values()不是通过父类继承的 本文转载自博客 - Java枚举类型, 博主对原文内 ...

  7. Spring Boot入门(五):使用JDBC访问MySql数据库

    本系列博客记录自己学习Spring Boot的历程,如帮助到你,不胜荣幸,如有错误,欢迎指正! 在程序开发的过程中,操作数据库是必不可少的部分,前面几篇博客中,也一直未涉及到数据库的操作,本篇博客 就 ...

  8. PHP开发模式之-单例模式

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Db{ private function __construct(){} private static ...

  9. cesium 之加载地形图 Terrain 篇(附源码下载)

    前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 内 ...

  10. 从一个git仓库拷贝到另一个git仓库

    利用git从一个仓库拷贝一个项目到另一个仓库,并且log也能够一起过去. 1.从原地址克隆一份裸版本库,比如原本托管于 GitHub. git clone --bare http://github.. ...