java - 基于用户配置的数据库并发问题的自定义序列
问题描述
在我们的项目中,我们创建了一个表(即 serial_conf),其中包含一些用户可以设置的配置。当我们需要为特定实体生成序列号(即 28_12_2019_0001)时,我们调用此序列号的服务类,该服务类上有@Transactional。
该服务正在执行以下操作:
- 从数据库中获取当前的串行配置
- 使用返回的对象生成序列号
- 更新序列号的计数器,以便下次我们可以生成正确的序列号并增加计数器(即 0002)
- 将结果字符串返回给服务的调用者
现在的问题是,当我使用 Jmeter 对 API 进行压力测试时(同时 10 个线程)。这个 API 做了很多操作,其中之一就是生成一个序列号。在 10 个线程中,只有 2 或 4 个通过,其他线程在串行服务的第 1 步抛出 OptimisticLockingFailureException。
我读过Spring @Transactional with synchronized 关键字在我尝试使用同步时不起作用,但它不起作用。我什至创建了一个单例类,它使用同步签名调用我的服务,所有使用串行服务的 API 现在都调用这个单例,这样它们就可以一个接一个地排列,但它也不起作用。
现在我的问题是:处理这个问题的正确方法是什么?我应该让数据库在表上做一个锁吗?还是应该使用不支持自定义样式的数据库序列(即 28_12_2019_0001)?
(我使用的是 spring-boot 1.5、hibernate 和 postgresql)
编辑1: 假设服务是这样的:
@Service("SerialService")
@Transactional(readOnly = false)
public class SerialService{
@Autowired
private SerialRepo repo;
public String generate(Long userId){
Serial serial = repo.findByUserId(userId).get();
serial.setCounter(serial.getCounter() + 1);
serial = repo.save(serial);
return "custom_serial_"+serial.getCounter().toString();
}
}
解决方案
我想这就是正在发生的事情:
- 线程A:进入
generate
方法,调用repo.findByUserId
并阻塞,等待结果。 - 线程B:进入
generate
方法,调用repo.findByUserId
并阻塞,等待结果。 - 线程 A:
repo.findByUserId
调用完成,返回Serial
aversion
= 1。 - 线程 B:
repo.findByUserId
调用完成,返回Serial
aversion
= 1。 - 线程 A:更新计数器并保存实体。版本更新为 2。
- 线程 B:更新计数器并尝试保存实体。由于版本不匹配而引发 OptimisticLockingFailureException。
同步该generate
方法没有帮助,因为在进入该方法之前(在 Spring 生成的代理中)打开了事务,因此两个线程看到相同的初始version
.
一般来说,假设冲突很少发生(因此名称:“乐观锁定”),通过重试操作来处理 OptimisticLockingFailureException 是很正常的。在您的情况下,它们非常频繁。
问题是您的测试是否反映了真实的应用程序使用情况。我假设他们使用相同的userId
. 我进一步猜测,Serial
每个用户都有单独的实例,因此它们是单独版本的。这意味着,仅当一个用户并行发送两个请求时才会抛出 OptimisticLockingFailureException。它可能会发生,但我猜它不应该那么频繁,所以重试是可以接受的。
我建议您使用不同的用户 ID 重复您的测试。
推荐阅读
- python - 有没有完整的枕头资料?(Python)
- javascript - 如何交换对象的键和值索引
- mysql - 如何将本地 nodeJS 服务器与在 AWS 上运行的 MySQL 连接?
- javascript - jquery div幻灯片效果与另一个div自动调整宽度
- javascript - 如何实现对象内部插槽属性
- python - Python程序获取一个人身高的数值和单位?
- android - Splash Screen is show but next screen app crashed logcat error is shown error something like this
- amazon-web-services - Amazon DynamoDB how to read populated Database in Golang?
- angular - Image Rendering features like Zoom, Rotate image using angular 6?
- sitefinity - Sitefinity - Revision history empty when publishing dynamic content item