深入MySQL——10

查询为何如此之慢

分为两种情况一种是查询后长时间不返回的,还有一种是查询很慢的

我们先来说第一种情况

长时间不返回

这种情况下就是锁阻塞导致不能返回,可以通过show processlist来查看语句处于什么状态,一般情况下会出现这几种状态:

1. waiting for table metadata

2. waiting for table flush

3. 上述两种表锁结束后进入行锁导致的

我们先来说说第一种

waiting for table metadata

也就是等MDL锁,所谓的MDL锁也叫做数据锁,保证多用户操作时的数据完整性。

比如A线程持有MDL写锁 B线程的select语句需要得到MDL读锁才能运行,所以B进入到了waiting状态。

这种情况下的解决方法就是找到这个持有写锁的线程将其kill掉。

可通过select blocking_pid from sys.schema_table_lock_waits;来解决其中blocking_pid通常指持有MDL写锁的id。

waiting for table flush

也就是等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为解决这种情况,通常使用加行锁来解决。

你可能感兴趣的:(mysql,数据库)