vlambda博客
学习文章列表

生产环境下到底应该如何优化MySQL锁等待?

及时获取有趣有料的技术文章

主题

聊聊 MySQL  InnoDB 的行锁,以及如何通过减少锁冲突。

什么是InnoDB 的行锁?

顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。

当然,数据库中还有一些没那么一目了然的概念和设计,这些概念如果理解和使用不当,容易导致程序出现非预期行为,比如两阶段锁。

从一个例子开始

场景:现在有两个事务,事务A和事务B。

如上图所示,事务A还没有进行commit,事务B这时候进行了update,此时大家猜一猜会发生什么?假设字段 id 是表 t 的主键。

这个问题的结论取决于事务 A 在执行完两条 update 语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:实际上事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。

知道了这个答案,你一定知道了事务 A 持有的两个记录的行锁,都是在 commit 的时候才释放的。

在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

那我们知道这个之后,对我们有什么帮助呢?其实可以很清晰的知道,如果你的事务中需要锁定多行,那么有可能就会造成锁冲突、尽量地将可能影响并发度的锁往后放。

继续一个案例

目前在我们公司遗留的老系统中,有一个表的设计比较奇葩,同一时刻,存在一个仓的多个用户并发修改同一行的数据,导致业务高峰期产生很多的锁等待现象。事务经常超时,从业务日志中经常输出Lock wait timeout exceeded; try restarting transaction

对于这个问题我们应该如何解决呢?

方案一:可以调大MySQL  innodb_lock_wait_timeout这个参数的值,其默认值是50,但是带来的弊端就是依然某些情况下必然出现继续超时的情况,解决不了根本问题,只能用于某些特别紧急的临时场景。

方案二:从设计角度去解决,将这个设计不当的表进行改造,做成按照用户为维度的设计,这样确保每个用户只修改自己的记录即可,这样便也对问题进行了根治,涉及到一些代码的改动。

总结

今天为大家分享了MySQL InnoDB 的行锁和两阶段锁协议。总结处理锁等待超时问题的原则就是:对同一行记录的并发更新进行拆分,减少锁冲突。InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。

建议

开发中,尽可能将大事务拆分为小事务,减少锁定的资源量和锁定的时间长度;尽可能减少基于范围的数据检索过滤条件,避免不该锁定的记录;合理设计索引,让InnoDB在索引键上面加锁的时候尽可能准确,尽可能地缩小锁定范围,避免造成不必要的锁定而影响其他Query的执行;尽可能让所有的数据检索都通过索引来完成,从而避免InnoDB因为无法通过索引键加锁而升级为表级锁定。





 

 

 

 

 


如果资源对你有帮助的话


        
          
          
        
❤️ 给个 「在看」 ,是最大的支持