MySQL实战45讲-07 行锁功过:怎么减少行锁对性能的影响?
MySQL 的行锁是在引擎层由各个引擎自己实现的。
MyISAM 引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁。
定义:行锁就是针对数据表中行记录的锁。
两阶段锁
两阶段锁的定义:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。
示例:
事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
死锁和死锁检测
死锁的定义:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。
当出现死锁以后,有两种策略:
- 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout来设置。
- 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
innodb_lock_wait_timeout值太大容易错过死锁;innodb_lock_wait_timeout值太小,容易对业务造成误伤。
innodb_deadlock_detect能够快速发现并进行处理的,但是它也是有额外负担的。
每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n) 的操作。假设有 1000 个并发线程要同时更新同一行,那么死锁检测操作就是 100 万这个量级的。虽然最终检测的结果是没有死锁,但是这期间要消耗大量的 CPU 资源。
怎么解决由这种热点行更新导致的性能问题呢?
- 一种头痛医头的方法,就是如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。而关掉死锁检测意味着可能会出现大量的超时,这是业务有损的。
- 另一个思路是控制并发度。
- 在客户端做并发控制:方法不可行,因为客户端很多。
- 中间件实现。
- 服务端实现,修改 MySQL 源码。基本思路就是,对于相同行的更新,在进入引擎之前排队。
作者:lee2guang
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.