MySQL行锁
Posted wgchen~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL行锁相关的知识,希望对你有一定的参考价值。
场景
行锁是针对数据表中行记录的锁,MySQL行锁是引擎层实现的,不是所有的引擎都支持行锁,MyISAM 引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,InnoDB是支持行锁的,这也是MyISAM 被 InnoDB 替代的重要原因之一。
两阶段锁
以上面表格中的例子,在事务 A 执行没有进行commit之前,事务B的update 语句会进行阻塞,直到事务A执行commit之后,事务B才能进行提交。也就是说,在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是做完操作就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
两阶段锁,锁的添加与释放分到两个阶段进行,之间不允许交叉加锁和释放锁。也就是在事务开始执行后为涉及到的行按照需要加锁,但执行完不会立刻释放,而是事务结束后统一释放。
思考
这对使用事务有什么帮助呢?
如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。如果把并发度影响较高的语句放在前面的话,可能会造成事务之间锁等待的时间较长,从而影响并发度。所以要放在操作的最后,这样最大程度减少事务之间的锁等待,提升并发度。
死锁和死锁检测
死锁是 当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待线程释放资源时,就会导致这几个线程进入无限循环等待的状态。
这时候,事务A在等待事务B释放id = 2 的行锁,而事务B在等待事务A释放id = 1 的行锁。事务A和事务B都在等待对方释放锁,就进入了死锁。
出现死锁后,有两种策略:
第一种:进入等待,直至超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 进行设置。
第二种:
发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
如果使用第一种策略,进入等待,默认的超时时间为50秒,意味着出现思索后,第一个被锁的线程要等待50秒之后才能超时退出,其他业务才能继续执行,这个等待时间,大大会影响性能,在生产环境是绝对不允许的。
那可不可以将这个时间设置为很小的值呢?
比如1秒。这样当出现死锁的时候,确实很快就可以解开,但如果不是死锁,而是简单的锁等待呢?所以,超时时间设置太短的话,会出现很多误伤。
所以第一种策略有点不大妥当,正常情况下还是采用第二种策略,进行死锁检测。如果有死锁发生,是能快速发现并且进行处理的,但是它也有额外的负担。
可以想象一下,每个事务被锁的时候,都要进行查看所依赖的线程有没有被别的线程锁住,如此循环,最后判断是否出现了死锁。
思考
按照上面的策略操作了,为什么有时候我服务器CPU消耗近100%,但是数据库却没有执行几个事务啊?
假设有1000个并发线程要同时更新同一行,那么死锁检测就是100量级的。为什么是100万量级呢?因为每一个都需要检测其他999个线程,整体消耗是O(N2)级别的。试想一下,单个线程加锁时的死锁检测时间是O(N),N个并发的死锁检测就是O(N2)。
并发更新同一行的1000个线程,整体耗费的死锁检测操作为1000*1000=100万。
为什么是个乘法——并发更新此行R1的某单个线程Tx,其所作的死锁检测工作为,Tx会有查看锁持有情况,耗费1000此操作——a.查看自身持有的行锁; b.遍历其他999个线程所持有的行锁,总共为1+999=1000次。
为什么会遍历其他999个线程,而不是仅看当前持有R1行锁的这个线程就行了?—— 因为行锁排队。
某线程Tm排队获取R1行锁,排在Tx前。如果Tx当前持有行锁R2,过会Tm先于Tx获持R1后,会变成——Tm持有R1,等待R2 && Tx持有R2,等待R1——Tm和Tx成环死锁。因此并发更新同一行的有N个线程,对应的死锁检测耗费代价为O(N2) ! 死锁检测不可避免,为防止死锁检测代价过高引起性能问题——想办法减少同时对同一行的更新的并发并发度。即降低N值。
热点数据性能问题
那我们该如何处理热点数据更新导致的性能问题呢?
要知道症状是什么?
死锁检测需要消耗大量的CPU资源。
1、如果能确保业务一定不会出现死锁,可以临时将死锁检测关闭。但是会带来一定的风险,因为业务设计的时候死锁并不是一个严重的错误,出现死锁了就进行回滚,通过业务重试一般就没问题了,对业务是无损的。关掉死锁检测可能导致大量超时,这是业务有损的
2、控制并发度,比如同一行同时最多只有 10 个线程在更新,那么死锁检测的成本很低,就不会出现这个问题。控制并发量不但可以减少死锁的概率,也可以减少死锁检测带来的性能消耗。
小结问题汇总
1、两阶段锁的概念是什么? 对事务使用有什么帮助?
2、死锁的概念是什么? 举例说明出现死锁的情况。
3、死锁的处理策略有哪几种?
4、等待超时处理死锁的机制什么?有什么局限?
5、死锁检测处理死锁的机制是什么? 有什么局限?
6、有哪些思路可以解决热点更新导致的并发问题?
以上是关于MySQL行锁的主要内容,如果未能解决你的问题,请参考以下文章