查询长时间不返回

在表t执行下面的sql语句:

select * from t where id=1;

结果查询时间长时间不反悔。一般碰到这种情况,大概率是表t被锁住了,分析原因的时候,一般首先执行show processlist命令,查看当前语句处于什么状态

然后根据每种状态,取分析他们产生的原因、如何复现、以及如何处理。

等MDL锁

使用show processlist命令查看到Waiting for table metadata lock。

这个状态表示的是,现在有一个线程正在表t上请求或者持有MDL写锁,把select语句堵住了

解决这类问题的方法就是找到谁持有MDL写锁,然后把他kill掉。

先执行语句 select * from performance_schema.setup_instruments where name='wait/lock/metadata/sql/mdl',看一下ENABLED和TIMED是不是都是YES,只有两个都是YES的时候才能执行下面的操作。

查询sys.schema_table_lock_waits这张表,找到造成阻塞的process id,把这个连接用kill命令断开即可。

select blocking_pid from sys.schema_table_lock_waits;

等flush

show processlist的状态是Waiting for table flush。表示现在正有一个线程要对表t做flush操作。

MySQL里面对表做flush操作的用法一般有两个:

flush tables t with read lock;
flush tables with read lock;

指定表t表示只关闭表t,否则全部关闭。

但是正常这两个语句执行都很快,除非他们也被其他线程堵住了。

所以这个状态出现的情况可能是:有一个flush tables命令被别的语句堵住了,然后它又堵住了我们的select语句

等行锁

要读取的行上,如果已经有一个事务在这行记录上持有一个写锁时,select语句就会被堵住。

解决方法就是找到谁占用了这个行的写锁,查询方法是:

mysql> select * from t sys.innodb_lock_waits where locked_table='`test`.`t`'\G

找到之后,使用kill关掉即可。

查询慢

这里的慢并不是要等待锁资源这类的慢,而是执行过程中的慢。

一个最简单的例子就是

select * from t where c=50000 limit 1;

而c字段上没有索引,这个语句只能走id主键顺序扫描,因此需要扫描50000行,扫描行数多,执行慢,这个很好理解

但是现在有一条只扫描一行,但是执行也很慢的语句

select * from t where id = 1;

但是使用下面的写法,就很快的语句

select * from t where id = 1 lock in share mode;

那是因为事务是这样的: A启动以一个事务,然后B执行update语句执行了100万次,生成了100万个回滚日志(undo log)。这样要读取100万个undo log之前的版本的数据,执行效率自然就慢了。

而lock in share mode是当前读,直接读取当前的结果,所以速度很快。