数据库中的悲观锁与乐观锁

阿里巴巴开发手册v1.4.0
【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。 说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。

1、数据库乐观锁

1.1 定义

系统认为数据的更新在大多数情况下是不会产生冲突的,只在数据库更新操作提交的时候才对数据做冲突检测。如果检测的结果与预期数据不一致,则返回失败。

1.2 实现方式

在设计数据库表时增加一个版本号字段version,更新之前先获取version的值,提交的时候带上已获取的version值与当前version值作比较,如果不相等则说明version值发生了变化,本次操作执行失败;如果相等则操作执行成功,则version会同时加1。

例如:

update table set columnA = 1,version=version+1 where id=#{id} and version = #{oldVersion}
1.3 优缺点
1.3.1 优点

由于检测冲突时并不依赖数据库本身的锁机制,请求的性能影响较小,当并发量较小的时候只有少部分请求会失败。

1.3.2 缺点

对数据库表的增加额外的字段,增加了数据库的冗余;另外,当请求并发量高的时候,version值在频繁变化,则会导致大量请求失败,影响系统的可用性。

2、数据库悲观锁

2.1 定义

对数据进行操作更新时,对操作持悲观保守的态度,认为产生数据冲突的可能性很大,需要先对请求的数据加锁再进行相关的操作。

2.2实现方式

通过数据库锁机制实现,即对查询语句添加for update关键字。

例如:

select * from table where id = 1 for update
2.3 优缺点
2.3.1 优点

每行数据的访问都是独占的,只有当正在访问该行数据的请求事务提交之后,其它请求才能依次访问该数据,否则将被阻塞等待获取锁。保证了数据的安全性。

2.3.2 缺点

每次请求都会额外产生加锁的开销,且未获取到锁的请求会被阻塞等待,在高并发环境下,容易造成大量请求阻塞,影响系统可用性。另外,悲观锁使用不当还可能产生死锁的情况。