如果 MySQL 数据库本身就有很大的压力,导致数据库服务器 CPU 占用率很高或 ioutil(IO 利用率)很高,这种情况下所有语句的执行都有可能变慢
今天要讨论的是一个简单的查询语句,数据库本身压力并不大的情况
第一类:查询长时间不返回:
执行语句:select * from t where id=1;(总共10万条记录)
一般碰到这种情况的话,大概率是表 t 被锁住了。接下来分析原因的时候,一般都是首先执行一下 show processlist 命令,看看当前语句处于什么状态。
1.1)等 MDL 锁
使用 show processlist 命令查看 Waiting for table metadata lock。
session A 通过 lock table 命令持有表 t 的 MDL 写锁,而 session B 的查询需要获取 MDL 读锁。
所以,session B 进入等待状态。这类问题的处理方式,就是找到谁持有 MDL 写锁,然后把它 kill 掉。
通过查询 sys.schema_table_lock_waits这张表,我们就可以直接找出造成阻塞的 process id,把这个连接用 kill 命令断开即可。
1.2)等 flush
出现 Waiting for table flush 状态的可能情况是:
有一个 flush tables 命令被别的语句堵住了,然后它又堵住了我们的 select 语句
flush tables t with read lock; (只锁表T)
flush tables with read lock;(锁所有表)
session A 中,故意每行都调用一次 sleep(1),这样这个语句默认要执行 10 万秒,在这期间表 t 一直是被 session A“打开”着。
session B 的 flush tables t 命令再要去关闭表 t,就需要等 session A 的查询结束。
session C 要再次查询的话,就会被 flush 命令堵住了。
1.3)等行锁
上面是表级锁的考验,接下来进入到数据库引擎里了。
mysql> select * from t where id=1 lock in share mode;
由于访问 id=1 这个记录时要加读锁,如果这时候已经有一个事务在这行记录上持有一个写锁,我们的 select 语句就会被堵住。
问题是怎么查出是谁占着这个写锁。如果你用的是 MySQL 5.7 版本,可以通过 sys.innodb_lock_waits 表查到。
mysql> select * from t sys.innodb_lock_waits where locked_table='`test`.`t`'\G
第二类:查询慢
经过了重重封“锁”,我们再来看看一些查询慢的例子。
2.1)查询扫描行数多:
mysql> select * from t where c=50000 limit 1;
由于字段 c 上没有索引,这个语句只能走 id 主键顺序扫描,因此需要扫描 5 万行。作为确认,你可以看一下慢查询日志。扫描行数多,所以执行慢,这个很好理解
2.2)只扫描一行,但是执行很慢的语句:
mysql> select * from t where id=1;
虽然扫描行数是 1,但执行时间却长达 800 毫秒。
另外一个语句select * from t where id=1 lock in share mode,执行时扫描行数也是 1 行,执行时间是 0.2 毫秒
按理说 lock in share mode 还要加锁,时间应该更长才对啊。
带 lock in share mode 的 SQL 语句,是当前读,因此会直接读到 1000001 这个结果,所以速度很快;
select * from t where id=1 这个语句,是一致性读,因此需要从 1000001 开始,依次执行 undo log,执行了 100 万次以后,才将 1 这个结果返回。




