我在使用 SQL Server 和 java 时遇到了并发问题。 我已经设置了一个消息队列,并有一个系统可以从该队列中读取数据。对于每条消息,都会发生以下情况:
- 消息包含“数量属性”。读取此属性并用该数字更新 table1 中的一行。
- 另一个表,table2 包含“总计”属性,并读取一行以获取总计值。 table1 和 table2 行可以使用 Id 进行映射。
- 通过添加消息中的数量属性来更新表 2 行。
这里的问题是,当我有多个服务实例并且两个实例都收到处理同一行的消息时,它们会向 table2 写入不同的值。这是一种读/写模式。
示例:
我在 table2 上得到了这个:
+------+-------+
| id | total |
+------+-------+
| 1 | 100 |
+------+-------+
| 2 | 100 |
+------+-------+
并收到两条消息,要求在 table1 上插入两行:
+------+-------+
| id | qty |
+------+-------+
| 1 | 10 |
+------+-------+
+------+-------+
| id | qty |
+------+-------+
| 1 | 50 |
+------+-------+
a) 消息 1 到达并尝试更新表 2 中的总计,它读取 100 并将总计更新为 110 b) 消息 2 到达并尝试更新总计,因为第一条消息尚未完成,所以它读取的总计为 100,并将其更新为 150。 c) 我们预计总数为 160 (100+10+50),但我们得到了 150,所以状态不正确。
有什么可以用来解决这个并发问题吗?
最佳答案
所以,在行上加锁是有必要的。我建议使用数据库锁来实现双重检查锁 - https://en.wikipedia.org/wiki/Double-checked_locking )。
1 - 启动事务(例如服务方法上的 @Transactional 注释)
2 - 使用 PESSIMISTIC_WRITE 锁定模式从数据库检索实体(确保指示 hibernate 应读取新副本而不是存储在 session 缓存中的副本)
3 - 在字段上执行更改/更新
4 - 保存实体(如果您不想等待自动刷新,请确保将值刷新到数据库)
5 - 提交事务(使用@Transactional时自动完成)
当您的事务在目标实体/数据库行上持有锁时,防止其他事务在您的更新正在进行时读取它。
实现如下:
@Transactional
public void updateValue(int id) {
final Session session = this.sessionFactory.getCurrentSession();
final Table1 table = session.get(Table1.class, id,LockMode.PESSIMISTIC_WRITE);
session.refresh(table);
table.setCount(table.getNoteCount()+1);
session.saveOrUpdate(table);
session.flush();
}
希望这有帮助。
关于java - 使用java和mysql服务器的读/写模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60554676/