点击上方 IT牧场 ,选择 置顶或者星标
技术干货每日送达!
主题
聊聊 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因为无法通过索引键加锁而升级为表级锁定。
干货分享
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术群讨论
近期热文
•LinkedBlockingQueue vs ConcurrentLinkedQueue•解读Java 8 中为并发而生的 ConcurrentHashMap•Redis性能监控指标汇总•最全的DevOps工具集合,再也不怕选型了!•微服务架构下,解决数据库跨库查询的一些思路•聊聊大厂面试官必问的 MySQL 锁机制
关注我
喜欢就点个"在看"呗^_^