更新用户金额伪代码
long start = System.currentTimeMillis();
long timeout = 3 * 1000L;
while(true) {
if(获取分段分布式锁成功) {
try {
User user = getUser(id);
Long beforeUpdateTime = user.getUpdateTime();
user.setMoney(user.getMoney() + 10); // 给用户加钱
int count = updateUser(user, beforeUpdateTime); // 乐观锁更新用户金额
if(count > 0) {
// 记录日志
}else {
// 没更新成功,继续尝试
}
}finally{
boolean releaseLock = // 释放分布式锁
if(!releaseLock) {// 抛出运行时异常}
}
}
if(System.currentTimeMillis() - start >= time) {
// 超时,抛出运行时异常
}
}
以上是段用分布式锁和数据库乐观锁的方式更新用户金额;代码本身没什么问题,可是后期表现处理的却是出现很多超时处理失败回滚的现象;
经过缜密的分析发现,循环里getUser(id)获取的数据不是数据库最新的,导致update乐观锁更新失败; 原因跟数据库的事务隔离级别有关; 由于使用的是默认的隔离级别,这里mysq默认的事务隔离级别是REPEATABLE-READ ;即可重复读;
mysql一共提供四种隔离级别;
- READ-UNCOMMITTED 可读未提交(即一个事务可以读取到另一个事务已经修改但未提交的数据)
- READ-COMMITTED 读已提交(即一个事务读取到的数据是其他事务已经提交的数据,未提交的数据不可见)
- REPEATABLE-READ 可重复读(即一个事务开启后同一个查询的结果始终一致, 即使过程中数据被其他事务修改)
- SERIALIZABLE 串行(即事务是串行执行,不会有并发问题,但是效率低)