【问题标题】:How to prevent update anomalies with multiple clients running non-atomic computations concurrently in PostgreSQL?如何防止多个客户端在 PostgreSQL 中同时运行非原子计算的更新异常?
【发布时间】:2019-02-11 00:41:55
【问题描述】:

我正在运行三个使用复制(1 个主,2 个从)的 PostgreSQL 实例,它们由两个单独的服务器访问:

  • 第一个(未公开的)服务器基本上会遍历特定表中的每一行,并在每个滴答声(基于这些资源的生产率)为每个用户持续更新特定的列(资源)。
  • 第二个服务器是一个公共 API,它公开了各种功能,例如消耗一定数量的资源。

为了访问和操作数据,我使用了一个 ORM 库,它允许我编写如下代码:

const resources = await repository.findById(1337);
// some complex computation
resources.iron = computeNewIron(resources.iron);
await repository.save(resources);

当然,当处理滴答的服务器试图更新资源量时,API 可能会想要扣除特定数量的资源,这可能导致任何一个服务器假定一定数量的资源,即不正确,基本上是典型的 UPDATE 异常。

我的问题是我不只是在写一个“简单”的原子查询,比如UPDATE table SET iron = iron + 42 WHERE id = :id。 ORM 库在内部使用不自引用各个列的直接赋值,这会产生类似于 UPDATE table SET iron = 123 WHERE id = :id 的内容,其中之前已计算了数量。

我可以假设,如果我使用手动编写的查询,这些查询通过自引用原子地增加/减少值,则可以防止上述异常。我想知道哪些其他选项可以缓解这个问题。我应该将我的 SELECT/computation/UPDATE 包装在事务中吗?够了吗?

【问题讨论】:

    标签: database postgresql transactions concurrentmodification


    【解决方案1】:

    您的问题有点不清楚,但是如果您的事务跨越多个语句,但需要具有数据库的一致状态,则基本上有两种选择:

    1. 使用悲观锁定:从数据库中读取值时,使用SELECT ... FOR UPDATE。然后这些行在您的事务期间被锁定,并且没有并发事务可以修改它们。

    2. 使用乐观锁定:以REPEATABLE READ 隔离级别启动事务。然后,您会在整个事务期间看到数据库的一致快照。如果其他人在您阅读后修改了您的数据,您的UPDATE 将导致序列化错误,您将不得不重试该事务。

    如果冲突很少,乐观锁定会更好,而如果冲突很可能发生,悲观锁定更可取。

    【讨论】:

      猜你喜欢
      • 2013-10-07
      • 1970-01-01
      • 2021-03-03
      • 2016-06-07
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      相关资源
      最近更新 更多