行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。
两阶段锁协议
- 在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是
两阶段锁协议
。 - 如果你的事务中需要锁多个行,要把
最可能造成锁冲突、最可能影响并发度
的锁尽量往后放。
死锁
- 当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态。
死锁解决方案
- 通过参数
innodb_lock_wait_timeout
根据实际业务场景来设置超时时间
,InnoDB 引擎默认值是 50s。- 如果设置过长被锁住的线程要等待很长时间才会退出,设置超时时间如果过短又会出现误伤,所以一般会采用第二种策略。
- 即
主动死锁检测
,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect
设置为 on,表示开启这个逻辑(默认是开启状态)。- 但它也是有额外负担的,可以想象一下这个过程:每当一个事务被锁的时候,就要看看它所依赖的线程有没有被别人锁住,如此循环,最后判断是否出现了循环等待,也就是死锁。
如何解决热点行更新导致的性能问题?
- 如果你能确保这个业务一定不会出现死锁,可以临时把
死锁检测关闭
掉,但一般不建议这么做。 控制并发度
,对应相同行的更新,在进入引擎之前排队,这样在 InnoDB 内部就不会有大量的死锁检测工作了。- 一个直接的想法就是,在
客户端做并发控制
。但是,你会很快发现这个方法不太可行,因为客户端很多。我见过一个应用,有 600 个客户端,这样即使每个客户端控制到只有 5 个并发线程,汇总到数据库服务端以后,峰值并发数也可能要达到 3000。 - 因此,这个并发控制要做在
数据库服务端
。如果你有中间件,可以考虑在中间件实现;如果你的团队有能修改 MySQL 源码的人,也可以做在 MySQL 里面。基本思路就是,对于相同行的更新,在进入引擎之前排队。这样在 InnoDB 内部就不会有大量的死锁检测工作了。
- 一个直接的想法就是,在
- 将热更新的行数据拆分成逻辑上的多行来
减少锁冲突
,但是业务复杂度可能会大大提高。 - InnoDB 行级锁是通过
锁索引记录
实现的,如果更新的列没建索引是会锁住整个表的。
问题总结
- 两阶段锁的概念是什么?对事务使用有什么帮助?
- 死锁的概念是什么?举例说明出现死锁的情况。
- 死锁的处理策略有哪两种?
- 等待超时处理死锁的机制是什么?有什么局限?
- 死锁检测处理死锁的机制是什么?有什么局限?
- 有哪些思路可以解决热点更新导致的并发问题?
笔记来源于《极客时间:MySQL实战45讲:行锁功过:怎么减少行锁对性能的影响?》