数据库手动计数器增加问题

时间:2016-04-06 07:13:20

标签: java spring

我正在使用Java和Spring Boot开发REST API来管理购买和客户。在我的MySQL数据库中,我有一个表Purchase,其中包含一个存储唯一ticketId的列。它不是主键。

当添加新购买时(通过执行PUT请求),我根据请求中提供的数据创建新购买,获取最大ticketId,将其递增1并存储在数据库中。主键是自动递增的。

这是我的代码:

@Transactional
public boolean saveNewPurchase(PurchaseDTO data) {
  Purchase p = createPurchaseFromData(data);
  Long idTicket = purchaseDao.getMaxIdTicket();
  p.setIdTicket(idTicket + 1);
  save(p);
}

这里是否存在并发问题?假设两个PUT请求并行执行此方法,是否可以检索相同的最大idTicket,从而在保存第二次购买时违反唯一idTicket约束?

如果是这样,我该如何解决?是否可以使方法synchronized解决问题?

感谢。

2 个答案:

答案 0 :(得分:1)

是的,它肯定容易出现并发问题。一个建议是使用AtomicInteger将计数器保留在内存中,这样你就不会最终从DB获得当前的maxId,这可能会导致竞争条件。因此,当应用程序启动时,通过查询数据库,它可以将maxId存储在内存中。这是强大的,即使在任何崩溃的情况下,它总是可以从DB读取信息。

这在分布式环境中不起作用,在这种情况下,有另一个专用于存储计数器的表,是JamieB在评论中建议的更好的方法。

答案 1 :(得分:1)

是的,这里存在并发问题。两个不同的主题可以获得相同的maxIdTicket,然后使用相同的ticketId保存购买。

我在这里看到3个解决方案:

  • 使用已同步
  • 使用AtomicInteger将计数器保留在内存中
  • 在MySQL中使用一个特定的表,只有一列AUTO_INCREMENT,每次需要一个计数器值时插入/获取一行。使用其他RDBMS,您可以使用序列,但我很确定MySQL中没有序列。

前2个解决方案在分布式环境中不起作用,所以我会做第三个。