1.两阶段锁
在下面的操作序列中,事务B的update语句执行时会是什么现象呢?假设字 段id是表t的主键。
这个问题的结论取决于事务A在执行完两条update语句后,持有哪些锁,以及在什么时候释放。 你可以验证一下:实际上事务B的update语句会被阻塞,直到事务A执行commit之后,事务B才 能继续执行。
知道了这个答案,你一定知道了事务A持有的两个记录的行锁,都是在commit的时候才释放的。 也就是说,在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
2.死锁和死锁检测
问题:如果这个影院做活动,可以低价预售一年内所有的电影票,而且这个活动只做一天。于是在活动 时间开始的时候,你的MySQL就挂了。你登上服务器一看,CPU消耗接近100%,但整个数据库 每秒就执行不到100个事务。这是什么原因呢?
(1)死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。这里我用数据库中的行锁举个例子。
a.一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout来设置。
b.另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。
show variables like 'innodb_lock_wait_timeout'; //查看超时等待配置
show variables like 'innodb_deadlock_detect'; //查看死锁检测配置
两种方案对比:
在InnoDB中,innodb_lock_wait_timeout的默认值是50s,意味着如果采用第一个策略,当出现 死锁以后,第一个被锁住的线程要过50s才会超时退出,然后其他线程才有可能继续执行。对于 在线服务来说,这个等待时间往往是无法接受的。
但是,我们又不可能直接把这个时间设置成一个很小的值,比如1s。这样当出现死锁的时候,确 实很快就可以解开,但如果不是死锁,而是简单的锁等待呢?所以,超时时间设置太短的话,会 出现很多误伤。
所以,正常情况下我们还是要采用第二种策略,即:主动死锁检测,而且 innodb_deadlock_detect的默认值本身就是on。主动死锁检测在发生死锁的时候,是能够快速发 现并进行处理的,但是它也是有额外负担的。