怎么减少行锁对性能的影响
在一个事务占有行锁时,另外一个事务必须等到第一个事务释放行锁,才能对该行进行操作.
事务B会一直阻塞,直到事务A完成后释放行锁,
在innodb事务中,行锁是在需要的时候才加上的,并不是不需要了就立即释放,而是等到事务结束,也即是commit之后才释放,这就是两阶段锁协议
怎么减少行锁对性能的影响:
如果一个事务中需要锁多行, 把最有可能发生锁冲突的,影响并发的锁,尽量往后放,比如实现一个电影院的在线交易业务,业务需要设计以下操作
- 扣除顾客A的账户余额
- 电影院余额加上A购买的票价
- 记录日志交易
完成这笔交易需要更新两条记录,为了保证交易的原子性,需要把这三个操作放在一个事务中,此时如果顾客B也要买电影票,那么事务发生冲突的部分就是语句
2,修改电影院余额.根据两阶段协议,不论怎么安排语句,行锁都是事务提交之后才释放的,所以,如果把语句2放到最后,按照3,1,2的顺序执行,那么影响影院余
额行锁的时间就最少,最大限度的减少了事务之间的锁等待,提升了并发度.
减少锁等待时间也并不能完全解决问题, 这里 还要提到死锁检测
死锁的四个必要条件
- 互斥条件
- 请求和保持
- 不可剥夺条件
- 循环等待
事务A和事务B都在互相等待对方释放自己需要的行锁,进入了死锁状态,
这里有两种处理策略
- 超时等待,超时时间可以通过参数进行设置 innodb_lock_wait_timeout
- 发起死锁检测,发现死锁后,主动回滚死锁链条中的某个事物,让其他事物可以继续执行,参数innodb_deadlock_detect设置为on表示开启死锁检测功能
innodb中,超时等待时间默认是50秒,如果对于在线服务来说,是不能承受的,所以一般我们使用主动死锁检测,而且innodb_deadlock_detect的默认值本来就是on,也即是开启状态.主动检测死锁,并且快速发现进行处理,也是有额外负担的
每当一个事务被锁的时候,就要去看它所依赖的线程有没有被别的锁住,如此循环,最后判断是否出现了循环等待,也就是死锁
每个新来的线程都要判断是否因为自己的加入,导致了死锁,假如有1000个并发线程要同时更新同一行,死锁检测操作就是100万数量级的,虽然最后检测的结果是没有死锁,但期间要消耗大量的CPU资源,就会出现CPU利用率很高,但是每秒却执行不了几个事务的情况.
避免过度消耗CPU的情况
- 如果能确保业务一定不会出现死锁,可以临时把死锁检测关闭
- 控制并发度, 使用中间件.对于相同行的更新,在进入引擎之前排队,这样在innodb内部就不会有大量的死锁检测工作了.