07 | 行锁功过:怎么减少行锁对性能的影响?
MySQL的行锁是引擎层在各个引擎自己实现的,但并不是所有的引擎都支持行锁。比如MyISAM引擎就不支持行锁,这意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行。InnoDB是支持行锁的。
行锁就是针对数据表中行记录的锁,比如事务A更新了一行,事务B也要更新同一行,那就必须等事务A的操作完成后才能进行更新。
两阶段锁
在下面的操作序列里,事务B的update语句执行时会什么现象?假设字段id是表t的主键。
这个问题的结论取决于事务A在执行完两条update语句后,持有哪些锁,以及在什么时候释放。实际上事务B的update语句会被阻塞,直到事务A执行commit之后,事务B才能继续执行。所以事务A持有的两个记录的行锁,都是在commit的时候才释放的。
在InnoDB事务里,行锁是在需要的时候才加上的,但是要等到事务结束才释放,这就是两阶段锁协议。
如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。举个例子来说明这段话:
在电影票在线交易业务里,顾客A要在电影院B购买电影票,这个业务可能需要涉及到下面的操作:
从顾客A账户余额中扣除电影票价
给影院B的账户余额增加这张电影票价
记录一条交易记录
整个过程需要update两条记录,insert一条记录,为了保证交易的原子性,需要把这三个操作放在一个事务里,如何安排三个语句在事务中的顺序呢?
如果这时候有顾客C也在影院B买票,事务冲突的部分就是语句2,因为他们需要更新同一个影院账户的余额,需要修改同一行数据。
根据两阶段锁协议,不管怎么安排语句顺序,所有的操作需要的行锁都是在事务提交的时候才释放,如果把语句2安排在最后,影院账户余额这一行的锁时间就最少,最大程度的减少了事务之间的锁等待,提升了并发度。