分为两种情况一种是查询后长时间不返回的,还有一种是查询很慢的
我们先来说第一种情况
这种情况下就是锁阻塞导致不能返回,可以通过show processlist来查看语句处于什么状态,一般情况下会出现这几种状态:
1. waiting for table metadata
2. waiting for table flush
3. 上述两种表锁结束后进入行锁导致的
我们先来说说第一种
也就是等MDL锁,所谓的MDL锁也叫做数据锁,保证多用户操作时的数据完整性。
比如A线程持有MDL写锁 B线程的select语句需要得到MDL读锁才能运行,所以B进入到了waiting状态。
这种情况下的解决方法就是找到这个持有写锁的线程将其kill掉。
可通过select blocking_pid from sys.schema_table_lock_waits;来解决其中blocking_pid通常指持有MDL写锁的id。
也就是等flush
我们来举例子实现这个过程
线程A一直对表进行添加或者修改操作
线程B想要flush该表
线程C select * from t where id =1;
因为flush操作需要关闭表,清除掉他的缓存区域,A线程直接在操作表阻塞了flush命令,导致C线程不能实现,这就是waiting for table flush。
解决这种情况的做法就是找到占用表的线程,将其kill掉就好。使用show processlist命令查看即可。
也就是等行锁
访问id=1的时候需要一个读锁,但是如果id=1现在存在一个写锁,select语句就会被阻塞住。
这个时候通过sys.innodb_lock.wits表查到是哪个线程,直接kill掉即可。
kill掉这个连接之后,它会自动回滚事务,释放出写锁。
我们首先得记住“坏查询不一定是慢查询”有可能是基数不够大,所以看不出来他是慢查询而已。
我们看看下面这种情况:
线程A进行查询
线程B进行update操作(update t set c=c+1 where id =1)
执行select *from t where id=1;
他会返回c=1这个结果,但是使用了800ms,只扫了一行的数据这是为什么??
如果我们执行select * from t where id=1 lock in share mode;
会很快的返回出c=10001
这是因为select *from t where id=1;是一致性读,所谓一致性读是指在这个情况下,事务得到一直的数据快照,即使其他事务对其进行修改,系统还是会根据事务开放的时间和版本数据进行返回。
而select * from t where id=1 lock in share mode;是当前读,直接返回当前c的数据。
一致性读返回的慢是因为,他对线程B的事务进行了回滚,回滚了一万次,直到得到最初的c=1才返回。
我们顺便来说说RR隔离和RC隔离
RR隔离是repeatable read 通过行锁和多版本并发控制(MVCC)来保证事务结束前所读取的数据不会被修改。防止脏读和不可重复读。
RC隔离是Read committed 是隔离级别最低的,保证读取已经提交的数据,读取数据时不加锁,可能会导致脏读。RC为解决这种情况,通常使用加行锁来解决。