定义
行锁就是针对数据表中行记录的锁
。- 行锁是在引擎层各个引擎自己实现的,并不是所有引擎都支持行锁,比如MyISAM就不支持行锁。
- 不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一表任何时刻只能由一个更新在运行。
两阶段锁
- 在InnoDB事务中,行锁是在需要时加上,在事务结束时才释放,这就是
两阶段锁协议
(如果执行完就释放,其他事务拿到该行的锁,去更新,然后提交。此时本事务回滚,最后的值会有问题。) 如果事务中需要锁多个行,要把最可能造成锁冲突,最可能影响并发度的锁尽量往后放。
死锁和死锁检测
- 当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
- 图片来自Mysql45讲07
- 事务A在等待事务B释放id=2的行锁,B在等待A释放id=1的锁,两个事务在互相等待对方的资源释放,就是进入了死锁状态
- 有两种策略:
- 第一种是直接进入等待,直到达到
innodb_lock_wait_timeout
设置的超时时间; - 第二种是发起死锁检测,主动回滚死锁链条中的某一条事务,让其他事务得以继续执行。将参数
innodb_deadlock_detect
设置为on开启这个逻辑。
- 第一种是直接进入等待,直到达到
- 正常情况下采用第二种方式,但是如果事务都要更新同一行,则会触发大量死锁检测,这期间消耗大量cpu。怎么解决由这种热点行更新导致的性能问题呢?
应用场景
- 用户抢购电影票:开启事务,1用户账号扣钱,2影院账号加钱,3记录日志,提交事务。
- 首先为了减少事务之间的锁等待,最好把2放到最后,因为行锁是在执行时申请,事务提交时释放。
- 如果很多人都在抢,则会有很多事务等待(因为大家都要等语句2那一行的行锁),死锁检测会消耗大量cpu。
- 不治本的方法:如果保证没有死锁,可以取消死锁等待。有风险。
- 控制并发度,在客户端不现实,在数据库服务端控制,对于相同行的更新,在进入引擎前排队(需要数据库专家。为何不在代码控制?)
- 可以考虑将影院账户从1行改为10行,账户总额等于10行相加。每次更新随机选其中一行,这样冲突概率变为十分之一,可以减少锁等待个数。