<a href="http://docs.spring.io/spring-data/couchbase/docs/2.1.1.RELEASE/reference/html/#version">点击查看原文</a>
Couchbase Server does not support multi-document transactions or rollback. To implement optimistic locking, Couchbase uses a CAS (compare and swap) approach. When a document is mutated, the CAS value also changes. The CAS is opaque to the client, the only thing you need to know is that it changes when the content or a meta information changes too.
Couchbase服务不支持多文档事务或者回滚。为了实现乐观锁,couchbase使用了cas(比较并交换)的方法。当文档发生变化,cas值也改变。cas对于客户端来说,是不透明的,你只需要知道当内容和元数据改变的时候cas值会改变。
In other datastores, similar behavior can be achieved through an arbitrary(任意的) version field with a incrementing(递增) counter. Since Couchbase supports this in a much better fashion, it is easy to implement. If you want automatic(自动的) optimistic locking support, all you need to do is add a @Version
annotation on a long field like this:
别的数据库,会使用一个任意的版本号和一个递增的计数器来实现乐观锁。但是couchbase使用了更加优良的方法,并且更容易实现。在一个long类型的字段上加上<code>@Version</code>注解,可以自动实现乐观锁。
<i>Example 13. A Document with optimistic locking.</i>
@Document
public class User {
@Version
private long version;
// constructor, getters, setters...
}
If you load a document through the template or repository, the version field will be automatically populated(填充) with the current CAS value. It is important to note that you shouldn’t access the field or even change it on your own. Once you save the document back, it will either succeed or fail with a OptimisticLockingFailureException
. If you get such an exception, the further approach(方法) depends on what you want to achieve(实现) application wise. You should either retry the complete load-update-write cycle or propagate(传播) the error to the upper layers for proper(适当的) handling.
如果你通过template或者repository加载一个文档,会自动把cas的值赋给<code>veriosn</code>字段。你不能去访问这个字段,或者自己去修改它。保存一个文档,要么成功要么失败,失败的时候,会抛出<code>OptimisticLockingFailureException</code>异常。当你看到抛出这个异常,怎么处理,要看你希望应用程序智能到什么程度。你可以重试,完成 <code>加载-更新-覆盖</code>这个动作 或者抛给上一层来处理。
<strong>tip:悲观锁、乐观锁</strong>
<em>悲观锁</em>
是针对操作(对于某个级别的数据的)的独占性来说的,在传统的数据库(如mysql)中,有针对库的锁、针对表的锁、针对行记录的锁。悲观锁的缺点是,举个例子来说,一行记录<code>12356346|Jon|36|Engineer</code>,如果使用了悲观锁,当我做了一个<code>update user set age=26 where id=12356346</code>操作后,此行数据锁被占用,其他对此行数据的操作会被阻塞。
<em>乐观锁</em>
乐观锁大多基于<code>数据版本</code>实现
1、读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。
2、将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据
<em>乐观锁的例子</em>
如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过 程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作 员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。
乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本 ( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。
读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。
1 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
2 在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
3 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。