使用java和ms sql服务器的读/写模式

问题描述 投票:0回答:1

我在使用SQL Server和Java时遇到了并发问题。我已经设置了一个消息队列,并且有一个可以从该队列中读取的系统。对于每条消息,都是这样:

  1. 消息包含“数量属性”。读取此属性,并使用该数字更新table1中的行。
  2. [另一个表,表2包含“总计”属性,并且读取一行以获取总值。 table1和table2行可以使用ID进行映射。
  3. 表2行通过添加消息中的数量属性来更新。

这里的问题是,当我有多个服务实例并且两个实例都收到一条消息来处理同一行时,它们将不同的值写入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,因此状态不正确。

我有什么可以用来解决并发问题的?

java sql sql-server concurrency read-write
1个回答
0
投票

因此,必须在行中添加锁。我建议使用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();
    }

希望这会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.