Read/write patter 使用 java 和 ms sql 服务器

Read/write patter using java and ms sql server

我在使用 SQL 服务器和 java 时遇到并发问题。 我已经设置了一个消息队列,并有一个系统可以从这个队列中读取。对于每条消息,情况如下:

  1. 消息包含 "quantity attribute"。读取该属性并使用该数字更新 table1 中的一行。
  2. 另一个 table,table2 包含一个 "total" 属性,并且读取一行以获得总值。 table1 和 table2 行可以使用 Id 进行映射。
  3. table2 行通过添加消息中的数量属性进行更新。

这里的问题是,当我有多个服务实例并且两个实例都收到一条消息来处理同一行时,它们将不同的值写入 table2。这是一种 read/write 模式。

示例:

我在 table2:

上看到了这个
+------+-------+
| id   | total |
+------+-------+
| 1    | 100   |
+------+-------+
| 2    | 100   |
+------+-------+

收到两条消息,要在 table1 上插入两行:

+------+-------+
| id   | qty   |
+------+-------+
| 1    | 10    |
+------+-------+

+------+-------+
| id   | qty   |
+------+-------+
| 1    | 50    |
+------+-------+

a) 消息 1 到达并尝试从 table2 更新总数,它读取 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 锁定模式从数据库中检索实体(确保指示休眠应该读取新副本而不是存储在会话缓存中的副本)

3 - 在字段

上执行 change/update

4 - 保存实体(如果不想等待自动刷新,请确保将值刷新到数据库)

5 - 提交事务(使用@Transactional 时自动完成)

当您的事务锁定目标 entity/db 行时,阻止其他事务在您的更新过程中读取它。

实现是这样的:

    @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();
    }

希望这对您有所帮助。