理解悲观锁与乐观锁

悲观锁

概念

维基百科这样解释:在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,PCC)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。

悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

从上述的解释可以知道,悲观锁是从一个悲观的角度看待用户操作,在用户想要操作数据时,悲观锁会“悲观”地认为其他用户也想要同时对同一数据进行操作,从而要求用户先获取锁,再进行操作,保证了用户操作的安全性。

优缺点

优点:

  • 通过“先取锁后访问”的保守策略,为数据处理的安全提供了保证。

缺点:

  • 对数据加锁会让数据库系统产生额外的开销,还增加了死锁的机会。
  • 在只读型事务处理中由于不会产生冲突,使用悲观锁,只会增加系统负载,降低并行性。

应用

1
2
select status from goods where id=1 for update; # for update 用于开启排它锁
update goods set status=2;

乐观锁

概念

维基百科这样解释:在关系数据库管理系统中,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,OCC)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自的那部分数据。在提交数据更新之前,每个事务都会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务更新的话,正在提交的事务会进行回滚。

乐观并发控制多用于数据争用不大、冲突较少的环境下,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因为可以获得比其他并发控制方法更高。

从上述的解释可以知道,相较于悲观锁,乐观锁决定从一个乐观的角度看待用户操作。在用户想要操作数据时,乐观锁会“乐观”地认为其他用户不会同时进行对同一数据进行操作的,而当用户对数据进行再次提交时,乐观锁才会对数据是否被修改进行检测,如果被修改过只能放弃当前操作。

优缺点

优点:

  • 乐观并发控制相信事务之间的数据竞争的概率比较小,所以在事务处理过程中不会出现任何锁,或产生死锁现象。

缺点:

  • 在系统并发量大的情况下,事务发生冲突的概率会大大增加,系统可用性会降低,用户体验也会随着操作不断失败而降低。

应用

在乐观锁中,一般会采用以下两种机制来记录数据的唯一性:

  • 数据版本(Version)。数据版本,即为数据增加一个版本标识,一般是通过为数据库增加一个数字类型的“version”字段来实现的。当读取数据时,将 version 字段的值一同读出,数据每更新一次,对此 version 的值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的 version 值进行比对,如果数据库当前版本号与第一次取出来的 version 值相等,则予以更新,否则认为是过期数据。
1
2
select status, version from goods where id=1;
update goods set status=2, version=version+1 where id=1 and version=version;
  • 时间戳(timestamp)。使用方式与数据版本相同,只是字段类型为时间戳而已。

顺带一提,Java并发中的 CAS 机制也是乐观锁机制。


参考资料

悲观并发控制

乐观并发控制