07 | 行锁功过:怎么减少行锁对性能的影响?
-
MySQL的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。
-
行锁就是针对数据表中行记录的锁 。
两阶段锁
-
两阶段锁协议:在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释 放,而是要等到事务结束时才释放。
-
如果你的事务中需要锁多个行,要把 最可能造成锁冲突、最可能影响并发度的锁尽量往后放 。
死锁和死锁检测
-
死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态。
-
出现死锁以后,有两种策略:
(1)直接进入等待,直到超时。这个超时时间可以通过参数
innodb_lock_wait_timeout
来设置。(2)发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数
innodb_deadlock_detect
设置为on
,表示开启这个逻辑。 -
在InnoDB中,
innodb_lock_wait_timeout
的默认值是50s , 但是又不可能直接把这个时间设置成一个很小的值,会出现很多误伤。 所以,正常情况下还是要采用第二种策略,即:主动死锁检测。 -
主动死锁检测,
innodb_deadlock_detect
的默认值本身就是on
。 能够快速发现并进行处理的,但是它也是有额外负担( 每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁 )。 -
怎么解决由这种热点行更新导致的性能问题呢?
- 问题的症结在于,死锁检测要耗费大量的CPU资源。
(1)如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。但是这种操作本身带有一定的风险。
(2) 控制并发度。在客户端做并发控制不太可行(因为客户端很多,汇总到数据库服务端后,峰值并发数也很高)。因此,并发控制要做在数据库服务端 ,可以考虑在中间件实现;也可以做在MySQL里面修改MySQL源码。基本思路就是,对于相同行的更新,在进入引擎之前“排队”。
- 能不能从设计上优化这个问题呢?
通过将一行改成逻辑上的多行来减少锁冲突。 以影院账户为例,可以考虑放在多条记录上,比如10个记录,影院的账户总额等于这10个记录的值的总和。这样每次要给影院账 户加金额的时候,随机选其中一条记录来加。这样每次冲突概率变成原来的1/10,可以减少锁等 待个数,也就减少了死锁检测的CPU消耗。
小结
- MySQL的行锁
- 两阶段锁协议
- 死锁和死锁检测
- 如何安排正确的事务语句
- 减少死锁的主要方向