在我们的项目中,我们创建了一个表(即serial_conf),该表具有用户可以设置的某些配置。 当我们需要为特定实体生成序列号(即28_12_2019_0001)时,我们将该序列的服务类称为 @Transactional 。
该服务正在执行以下操作:
现在的问题是,当我使用Jmeter对一个API(同时有10个线程)进行压力测试时。该API正在执行许多操作,其中之一是生成序列号。 在10个线程中,只有2个或4个通过,其他线程在串行服务的第1步抛出OptimisticLockingFailureException。
当我尝试使用 synchronized 时,我已经读过Spring @Transactional with synchronized keyword doesn't work,但没有用。我什至创建了一个单例类,该类使用同步签名调用我的服务,并且所有使用串行服务的API现在都调用此单例,以便它们可以彼此排成一行,但是它也不起作用。 / p>
现在我的问题是:处理此问题的正确方法是什么?我应该使数据库在表上执行锁定吗?还是应该使用不支持自定义样式(即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();
}
}
答案 0 :(得分:0)
我想这是正在发生的事情
generate
方法,调用repo.findByUserId
并阻塞,等待结果。generate
方法,调用repo.findByUserId
并阻塞,等待结果。repo.findByUserId
调用完成,返回Serial
= 1的version
。repo.findByUserId
调用完成,返回Serial
= 1的version
。同步generate
方法无济于事,因为在进入该方法之前(在Spring生成的代理中)打开了事务,所以两个线程都看到相同的初始version
。
通常,假定冲突很少发生(因此名称为“乐观锁定”),通过重试该操作来处理OptimisticLockingFailureException是很正常的。就您而言,它们很常见。
问题是您的测试是否反映了实际的应用程序使用情况。我假设他们使用相同的userId
执行10个并行请求。我进一步猜测,每个用户有单独的Serial
实例,因此它们分别进行了版本控制。这意味着,仅当一个用户并行发送两个请求时,才会引发OptimisticLockingFailureException。可能会发生,但是我猜应该不会那么频繁,所以重试是可以接受的。
我建议您使用不同的用户ID重复测试。