在分布式领域,熟悉一些基本的概念,比如,2PC,3PC,最近在优化一个项目的数据访问,接触到一个概念:2PL,两阶段锁, innodb引擎里面的加锁协议,保证单机事务的隔离和一致性。

例子

先看下面两个方案:

方案一:

1
2
3
4
5
6
7
8
begin;      
//1. 扣减库存
update item_stock set count=count-1 where id=${id} and count >= 1;
//2. 创建订单
insert into item_order;
//3. 通知发货
mq.send(notify_ship_message);
commit;

方案二:

1
2
3
4
5
6
7
8
begin;      
//1. 创建订单
insert into item_order;
//2. 通知发货
mq.send(notify_ship_message);
//3. 扣减库存
update item_stock set count=count-1 where id=${id} and count >= 1;
commit;

哪个方案更好?

实际上,这取决于数据库里面,那些是热点数据. 下面是2PL的执行过程:

Mysql执行事务的时候,会分为两个阶段执行加锁/解锁:

  • 在事务中只有提交(commit)或者回滚(rollback)时才是解锁阶段,
  • 其余时间为加锁阶段。

可以看出,方案一里面,对库存持有的锁的时间更长,因为加锁完毕后,还有等待对订单库的操作,而在我们的系统(商品中心),库存又是一个热点数据,那么, 自然更好的优化方案是第二个。

小结

理解2PL有助于更好的优化sql语句,尽量减少事务的粒度,除了提高性能外还可以减少死锁的可能性;不过要注意的是,2PL不能防止死锁,因为每次获得锁的操作还是分开的,可以看看:严格两阶段封锁协议和强两阶段封锁协议