前言

什么是行锁?

简单来讲,就是当一个事务A正在修改表中的一行数据时,会加锁,另外一个事务B在此期间想要修改,是不可行的,只能等。

什么时候行锁会释放呢?

在事务A执行commit操作之后,涉及到的行锁才会被释放?

怎么减少锁冲突提高性能?

例如有一单银行交易,初始是这么设计的:

  1. 从账户A扣100块钱
  2. 从账户B加100块钱
  3. 记录这笔交易日志

在这种情况下,进行第三条记录交易日志操作的时候,前两条占用的行锁仍然是持有的,这就加大了锁冲突的概率。

显而易见,优化的方式可以调整操作的顺序,比如顺序改成3、1、2,这样2操作涉及到的行锁不受影响,进行完操作就释放掉行锁了。

死锁

上面的一系列流程下来看起来很ok,但是没考虑循环等待可能导致的死锁情况:

在这里插入图片描述
在这种情况下,A等B释放行锁,B等A释放行锁,那就造成了死锁。

怎么解决呢?

  1. 不管他就好了,innodb引擎中有个innodb_lock_wait_timeout参数代表事务等待资源的超时时间,默认是50s,也就是说等待50s,如果没拿到想要的资源就退出,这个策略看起来就不靠谱,尤其是针对一些要求在线业务。
  2. 发起死锁检测,设置innodb_deadlock_detect参数为on,表示开启死锁检测。当发现死锁后,主动释放一个事务重试,从而让另外一个事务得以进行,这样就打破了循环等待。

看起来一切都ok了嘛?

死锁检测是有额外负担的,这个过程的时间复杂度是O(n),当1000个并发线程同时修改同一行时,那么死锁检测操作就是100w级的,效率太低。

怎么办?

可以考虑通过将一行改成逻辑上的多行来减少锁冲突。还是以银行交易为例,可以考虑放在多条记录上,比如 10 个记录,影院的账户总额等于这 10 个记录的值的总和。

这样每次要给影院账户加金额的时候,随机选其中一条记录来加。这样每次冲突概率变成原来的 1/10,可以减少锁等待个数,也就减少了死锁检测的 CPU 消耗。

秒啊!

参考

极客时间