我在使用SQL Server和Java时遇到了并发问题。我已经设置了一个消息队列,并且有一个可以从该队列中读取的系统。对于每条消息,都是这样:
这里的问题是,当我有多个服务实例并且两个实例都收到一条消息来处理同一行时,它们将不同的值写入table2。这是一种读/写模式。
示例:
我在表2上找到了它:
+------+-------+
| id | total |
+------+-------+
| 1 | 100 |
+------+-------+
| 2 | 100 |
+------+-------+
并且在表1上插入了两条消息以插入两行:
+------+-------+
| id | qty |
+------+-------+
| 1 | 10 |
+------+-------+
+------+-------+
| id | qty |
+------+-------+
| 1 | 50 |
+------+-------+
a)消息1到达并尝试从表2更新总数,它读取100并将总数更新为110b)消息2到达并尝试更新总计,因为第一条消息尚未完成,因此它也读取总计100,并将其更新为150。c)我们期望总数为160(100 + 10 + 50),但我们得到150,因此状态不正确。
我有什么可以用来解决并发问题的?
因此,必须在行中添加锁。我建议使用DB锁-https://en.wikipedia.org/wiki/Double-checked_locking)实现双重检查锁。
1-开始交易(例如,服务方法上的@Transactional注释)
2-使用PESSIMISTIC_WRITE锁定模式从数据库检索实体(确保指示休眠状态,应读取新副本而不是会话高速缓存中存储的副本)
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();
}
希望这会有所帮助。