案例背景
项目需求是生成一段自增的编号,由于编号涵盖不同字符,所以需要编写生成编号的逻辑
1. 问题初解决,加锁
在有该需求前提下,编写如下伪代码
@Transaction(rollbackFor = Exception.class)
public void test() {
//获取当前数据库编号信息并做一定处理得到生成的编号
//省去业务操作
String baseNum = "xxxx03";
//新生成编号保存至数据库
baseService.save(baseNum);
..
..
..
//业务代码
}
在上面情况下发现有编号重复现象,于是尝试加锁解决,因为是多服务,使用分布式锁Redission
,伪代码如下
@Transaction(rollbackFor = Exception.class)
public void test() {
RLock lock = redissionClient.getLock("lock");
boolean flag = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (flag) {
try {
//获取当前数据库编号信息并做一定处理得到生成的编号
//省去业务操作
String baseNum = "xxxx03";
//新生成编号保存至数据库
baseService.save(baseNum);
} finally {
lock.unlock();
}
}
..
..
..
//业务代码
}
2. Redission失效?
在使用上面方案后,发现还是会有重复编号出现,难道是Redission失效了?
这点应该不可能,于是开始对代码再次分析,整体代码只锁住了关于编号操作,是其他的模块导致的么?
于是试着将整块代码锁住,重复编号就没再出现。
回过头来,再次对代码分析,发现导致编号重复的是@Transaction
这个注解,这段代码整体是一个事务,而事务有ACID四大特性,事务彼此隔离,即不可见,导致编号的修改对于其他事务是不可见的,如图所示,整体可以分成两部分
在这段代码中,由于事务不可见的特性,在当前事务完成过程中其他线程读取到数据库的值其实还是未操作的数据,从而导致数据重复。
于是将编号生成单独拆分出一个事务进行处理,问题到此结束。
重新温习事务四大特性