第一类:查询被锁住(不返回结果)

如果执行一条查询语句长时间不返回,大概率是表被锁了,可以执行show processlist语句看看当前语句处于什么状态。

等MDL锁

回顾MDL锁

MDL锁全程为meta data lock元数据锁,分为MDL读锁和MDL写锁。

  • 当我们执行DDL语句时就需要获得MDL写锁
    • 回顾DDL语句获得MDL写锁之后会降级为MDL读锁,真正做完DDL之后再升级为写锁,再释放MDL锁
  • 当我们执行DML语句时就需要获得读锁
  • 读读不互斥,读写互斥
  • MDL锁在事务提交的时候才会释放

执行查一行的sql可能会出现的被锁住和慢执行的原因 - 图1
上边这种场景就会出现session B的查询语句因为获取不到MDL读锁一直不返回结果,session A通过lock table 命令获得表的MDL写锁。
image.png

解决办法

在my.inf中加入配置performance_schema=on,相比于设置为off会有10%左右的性能损耗。
执行select blocking_id from sys.schema_table_lock_waits; 查询出造成阻塞的process id然后杀掉。

等flush

flush 命令是关闭已经打开的表,执行的速度很快

执行查一行的sql可能会出现的被锁住和慢执行的原因 - 图3
其实就是flush命令被阻塞了,然后我们又去查询,就被flush命令阻塞了,解决方法和上边的一样

等行锁

例如下边这个SQL,加了共享锁(读锁)

  1. mysql> select * from t where id=1 lock in share mode;
  1. sessionA
begin;
mysql> select * from t where id=1 lock in share mode;

2.sessionB

UPDATE t SET a=0 WHERE id=1;

image.png
可以通过SQL查看是哪个连接锁住了表

SELECT * FROM sys.innodb_lock_waits WHERE locked_table='`test`.`t`';  \G

image.png
通过执行结果我们可以很清楚的看到是11这个进程,我们直接kill就行了。

第二类:查询缓慢

查询行数多

mysql> select * from t where c=50000 limit 1

由于字段c上没有索引,因此需要扫描五万行。
还有一种情况,就是当我们开启一个一致性读的时候,另一个线程把我们要查询的记录更改了n次,导致需要查询n次undo log才能找到我们事务能看到的结果。
执行查一行的sql可能会出现的被锁住和慢执行的原因 - 图6

  • sessionA首先开启一致性读事务之后,sessionB开始执行update语句
  • sessionB执行完一百万次update之后,id=1这条记录生成了100万个回滚记录
  • 由于带lock in share mode语句是当前读,只需读取最新的值即可,返回很快
  • 而第一个查询是一致性读,需要从1000001开始,依次执行undo log,执行100万次以后,才将1这个结果返回。
  • undo log中记录的值为把2改成1,把3改成2,这样的操作逻辑。