MySQL的行锁是在引擎层由各个引擎自己实现的。
但并不是所有的引擎都支持行锁,比如MyISAM引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。
InnoDB支持行锁,这也是MyISAM被InnoDB替代的重要原因之一。
顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务A更新了一行,而这时候事务B也要更新同一行,则必须等事务A的操作完成之后才能进行更新。
两阶段锁协议
在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段协议。
以下为例:
事务A | 事务B |
---|---|
begin; | |
update t set k=k+1 where id = 1; | |
update t set k=k+1 where id = 2; | |
begin; | |
update t set k=k+2 where id = 1; | |
commit; |
事务B在执行时会被阻塞住,要等到事务A提交之后才能继续执行。
两阶段锁带来的影响:如果把加锁的操作(更新操作)放在整个事务的所有操作的前面,且此时对同一行的并发操作比较多,那么就意味着行被加锁的时间相对于把这些加锁的代码放在后面要长,阻塞的总体时间比较长,如果加锁的代码放在最后,阻塞的时间就会相对缩短,并且距离锁释放的时间更近,可以提高整体性能。
建议:如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁的申请时机尽量往后放。
死锁和死锁检测
当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,成为死锁。
出现死锁的解决策略:
- 直接进入等待,直到超时。可以通过参数innodb_lock_wait_timeout来设置
- 发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。参数innodb_deadlock_detect 设置为on 开启逻辑。这会损耗一定性能。
对于热点更新,如果开启了死锁检测,会消耗大量的CPU资源。解决这种由热点行更新导致的性能问题可参考:
- 如果能确保某个业务不会出现死锁,则可以临时关掉死锁检测
- 控制并发,保证同一行数据不会有太多线程在同时操作
- 做业务逻辑改造。比如能不能放到缓存上,或者对热点行进行逻辑拆分。
网友评论