前言
什么是行锁?
简单来讲,就是当一个事务A正在修改表中的一行数据时,会加锁,另外一个事务B在此期间想要修改,是不可行的,只能等。
什么时候行锁会释放呢?
在事务A执行commit操作之后,涉及到的行锁才会被释放?
怎么减少锁冲突提高性能?
例如有一单银行交易,初始是这么设计的:
- 从账户A扣100块钱
- 从账户B加100块钱
- 记录这笔交易日志
在这种情况下,进行第三条记录交易日志操作的时候,前两条占用的行锁仍然是持有的,这就加大了锁冲突的概率。
显而易见,优化的方式可以调整操作的顺序,比如顺序改成3、1、2,这样2操作涉及到的行锁不受影响,进行完操作就释放掉行锁了。
死锁
上面的一系列流程下来看起来很ok,但是没考虑循环等待可能导致的死锁情况:
在这种情况下,A等B释放行锁,B等A释放行锁,那就造成了死锁。
怎么解决呢?
- 不管他就好了,
innodb
引擎中有个innodb_lock_wait_timeout
参数代表超时时间,默认是50s,也就是说等待50s,否则退出,这个策略看起来就不靠谱,尤其是针对一些要求在线业务。 - 发起死锁检测,设置
innodb_deadlock_detect
参数为on
,表示开启死锁检测。当发现死锁后,主动释放一个事务重试,从而让另外一个事务得以进行,这样就打破了循环等待。
看起来一切都ok了嘛?
死锁检测是有额外负担的,这个过程的时间复杂度是O(n)
,当1000个并发线程同时修改同一行时,那么死锁检测操作就是100w级的,效率太低。
怎么办?
可以考虑通过将一行改成逻辑上的多行来减少锁冲突。还是以银行交易为例,可以考虑放在多条记录上,比如 10 个记录,影院的账户总额等于这 10 个记录的值的总和。
这样每次要给影院账户加金额的时候,随机选其中一条记录来加。这样每次冲突概率变成原来的 1/10,可以减少锁等待个数,也就减少了死锁检测的 CPU 消耗。