阻止对数据库操作的并发Web服务调用

时间:2015-02-18 12:16:49

标签: java web-services concurrency

我有Spring WS调用,在那里我执行一些查找并最终决定更新现有数据或将新行插入数据库。问题是,当我同时在该端点上执行2次相同的插入调用时,insertData()方法在不同的线程中执行两次,第二次调用通常会导致SQLIntegrityConstraintViolationException,因为第一次执行已插入记录和第二次尝试使用相同的版本号进行相同操作。

我应该如何对方法调用执行某种限制策略,以便它们以某种顺序执行或放入队列?我还有一个特定的字段来检查/缓存调用。有什么建议吗?

1 个答案:

答案 0 :(得分:0)

您还没有指定您正在使用的数据库,但大多数数据库都有" upsert"功能在某种程度上。 Oracle,MySQL,PostgreSQL和许多其他人都支持它。如果数据存在于表中,则upsert操作原子更新数据,如果数据不存在于表中,则将其插入。通过这种方式,您可以使数据库处理并发问题。

如果你想以编程方式进行,这里有一个示例方法:

我假设您有某种唯一值,当您想要多次将其插入数据库时​​会导致违反约束。可以在此特定方法中使用该唯一值或任何其他唯一值。

    String uniqueKey = "someKey"; // This is your unique value that you get somehow

    ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();

    Object lock = locks.get(uniqueKey);

    if(lock == null) {
        Object previous = locks.putIfAbsent(uniqueKey, lock = new Object());
        if(previous != null) {
            lock = previous;
        }
    }

    synchronized (lock) {
        // you can safely check your database to do an update or an insert here. 

    }

如果两个线程尝试对同一个uniqueKey值进行操作,则它们最终会尝试进入同一个锁对象上的synchronized块。因此,它们将逐个执行同步块。但请注意,此方法不会从并发映射中删除锁定对象。这意味着您的内存消耗将随着每个新的uniqueKey值而增加,您可能需要找到一种方法来最终删除不必要的锁定对象。

另一种方法是使用许多队列来执行对数据库的操作。对于每个队列,您必须提供以下两个保证:

  • 操作K完成后必须执行操作K + 1。
  • 特定uniqueKey值的所有操作必须放在同一队列中。