Spring3+mysql5实现乐观锁时的问题总结-转
id seq version
1 1001 1
获取主键的方法大概是这样的:@Transactional
public synchronized String getKey(String type) {
//sql:select * from key_constnt where id=1
KeyConstantPO key = this.keyConstantDAO.queryById(1);
int seq = key.getSeq()+1;
key.setSeq(seq);
//sql:update key_constant set seq=#seq# where id=#id# and version=#version#
this.keyConstantDAO.update(keyMod);
return seq;
}
- 第1次思考
- 第2次思考
@Transactional
public String getKey(String type) {
int seq = 0;
KeyConstantPO key = null;
do{
//sql:select * from key_constnt where id=1
key = this.keyConstantDAO.queryById(1);
seq = key.getSeq()+1;
key.setSeq(seq);
//sql:update key_constant set seq=#seq# where id=#id# and version=#version#
} while(this.keyConstantDAO.update(key) == 0);
return seq;
}
update key_constant set seq=#seq#, version=version+1 where id=#id# and version=#version#
看到这里,你以为上面的改动必须会成功,那么,你错了,实际上还是失败的。- 第3次思考
update key_constant set seq=2, version=version+1 where id=1 and version=1 //这个是成功的
其它线程因为并发原因,第1次更新失败,因为它们也是上面这个Sql,这个可以理解;但是紧接着,它们会又去数据库中查询最新值,再更新seq,应该会可能成功的,但是实际上,它们在循环中每次查出来的数据都是跟第1次是一样的,就是说seq=1 version=1,明明其它线程(也可以认为是其它事务)已经提交了最新的更新,为什么这些线程查询老是旧值呢?我开始以为是ibatis有缓存什么的,后来查了,没有。//注解org.springframework.transaction.annotation.Transactional的isolation定义
Isolation isolation() default Isolation.DEFAULT; // 默认是DEFAULT
//继续跟踪代码 org.springframework.transaction.annotation.Isolation
/**
* Use the default isolation level of the underlying datastore.
* All other levels correspond to the JDBC isolation levels.
* @see java.sql.Connection
*/
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), //很明显,使用底层数据存储的默认隔离级别
//查看数据库版本
SELECT VERSION();
5.1.47-log
//查看mysql5的默认隔离级别
SELECT @@global.tx_isolation
REPEATABLE-READ
SQL标准用3个必须在并发事务中避免的现象定义了4个隔离级别:| 隔离级别 | 脏读(Dirty Read):读取了其它事务未提交的数据 | 不可重复读(NonRepeatable Read):读取了其它事务修改的数据 | 幻读(Phantom Read) :读取了其它事务增加/删除的数据 |
| 读未提交(Read uncommitted) | 可能 | 可能 | 可能 |
| 读已提交(Read committed) | 不可能 | 可能 | 可能 |
| 可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
| 串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
@Transactional(isolation=Isolation.READ_COMMITTED)
最后测试,OK。- 第4次思考
public String getKey(String type) {
}

浙公网安备 33010602011771号