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行锁的主要内容,如果未能解决你的问题,请参考以下文章

mysql表锁行锁索引之间暧昧的关系

MySQL ---- 共享锁独占锁行锁表锁

MySQL ---- 共享锁独占锁行锁表锁

mysql数据库表锁行锁

MySQL系列3 - 全局锁表锁行锁

MySQL系列3 - 全局锁表锁行锁