一、行锁:
1、定义:行锁就是针对数据表中行记录的锁。也就是说,某一事务在操作某一行时,此时该行会被“锁”起来,别的事务操作该行会被阻塞。直到该事务操作完成。比如事务A更新了一行,而这时候事务B也要更新同一行,则必须等事务A的操作完成后才能进行更新。
2、MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。InnoDB是支持行锁的,这也是MyISAM被InnoDB替代的重要原因之一。
3、两阶段锁协议:在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。(和事务中的MDL锁类似)。
4、“如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放”的原因:根据两阶段锁协议,不论你怎样安排语句顺序,所有的操作需要的行锁都是在事务提交的时候才释放的。所以,如果你把最可能造成锁冲突、最可能影响并发度的锁安排在最后,那么最可能造成锁冲突、最可能影响并发度的这一行的锁时间就最少。这就最大程度地减少了事务之间的锁等待,提升了并发度。
二、死锁和死锁检测:
1、死锁:指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
2、死锁的解决办法:
1)、直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。
2)、发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。
第一种解决办法的缺点:innodb_lock_wait_timeout的默认值是50s,当出现死锁以后,第一个被锁住的线程要过50s才会超时退出,然后其他线程才有可能继续执行。这个等待时间是非常长的。但也不能把innodb_lock_wait_timeout设置的非常小,因为如果不是死锁,而是简单的锁等待,就会出错。所以一般不使用它。
第二种解决办法的优点:nnodb_deadlock_detect的默认值本身就是on。主动死锁检测在发生死锁的时候,是能够快速发现并进行处理的。
第二种解决办法的缺点:有额外负担。你可以想象一下这个过程:每当一个事务被锁的时候,就要看看它所依赖的线程有没有被别人锁住,如此循环,最后判断是否出现了循环等待,也就是死锁。
3、如何解决热点行更新导致的性能问题?
1)如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关闭掉。一般不建议采用
2)控制并发度,对应相同行的更新,在进入引擎之前排队。这样在InnoDB内部就不会有大量的死锁检测工作了。
3)将热更新的行数据拆分成逻辑上的多行来减少锁冲突,但是业务复杂度可能会大大提高。
4、注意:
1) 一致性读不会加锁,就不需要做死锁检测;
2) 并不是每次死锁检测都都要扫所有事务。比如某个时刻,事务等待状态是这样的:B在等A,D在等C。 现在来了一个E,发现E需要等D,那么E就判断跟D、C是否会形成死锁,这个检测不用管B和A