我需要在博士后更新一组记录;我正在努力防止压力测试中出现的僵局.

解决这个问题的典型方法是按特定的顺序更新记录,例如按ID——但Postgres似乎不允许按顺序更新.

假设我需要更新,例如:

UPDATE BALANCES WHERE ID IN (SELECT ID FROM some_function() ORDER BY ID);

当同时运行200个查询时,会导致死锁.怎么办?

我在寻找一个通用的解决方案,而不是像UPDATE with ORDER BY年那样针对具体情况的解决办法

很显然,一定有比编写游标函数更好的解决方案.此外,如果没有更好的方法,光标的最佳功能会是什么样子?逐个记录更新记录

推荐答案

据我所知,没有办法直接通过UPDATE声明来实现这一点;确保锁顺序的唯一方法是使用SELECT ... ORDER BY ID FOR UPDATE显式获取锁,例如:

UPDATE Balances
SET Balance = 0
WHERE ID IN (
  SELECT ID FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
)

这样做的缺点是在Balances表上重复ID索引查找.在您的简单示例中,您可以通过在锁定查询期间获取物理行地址(由ctid system column表示)并使用该地址驱动UPDATE来避免这种开销:

UPDATE Balances
SET Balance = 0
WHERE ctid = ANY(ARRAY(
  SELECT ctid FROM Balances
  WHERE ID IN (SELECT ID FROM some_function())
  ORDER BY ID
  FOR UPDATE
))

(使用ctid时要小心,因为数值是瞬时的.我们在这里很安全,因为锁会阻止任何更改.)

不幸的是,规划师只能在一小部分情况下使用ctid(你可以通过在EXPLAIN输出中寻找"Tid扫描" node 来判断它是否有效).要在单个UPDATE语句中处理更复杂的查询,例如,如果您的新余额与ID一起由some_function()返回,则需要返回基于ID的查找:

UPDATE Balances
SET Balance = Locks.NewBalance
FROM (
  SELECT Balances.ID, some_function.NewBalance
  FROM Balances
  JOIN some_function() ON some_function.ID = Balances.ID
  ORDER BY Balances.ID
  FOR UPDATE
) Locks
WHERE Balances.ID = Locks.ID

如果性能开销是一个问题,您需要使用游标,它看起来像这样:

DO $$
DECLARE
  c CURSOR FOR
    SELECT Balances.ID, some_function.NewBalance
    FROM Balances
    JOIN some_function() ON some_function.ID = Balances.ID
    ORDER BY Balances.ID
    FOR UPDATE;
BEGIN
  FOR row IN c LOOP
    UPDATE Balances
    SET Balance = row.NewBalance
    WHERE CURRENT OF c;
  END LOOP;
END
$$

Postgresql相关问答推荐

到第二天的Postgres计时器

如何将 jackc/pgx 与连接池、上下文、准备好的语句等一起使用

如何在 postgres 中查询键设置为空值的 jsonb 字段

需要用 jack/pgx 更新 golang 中复合类型的 PSQL 行

postgresql中多个左连接的空结果

如何在postgresql中按时间查询

无法从外部连接到在 AWS EC2 实例上运行的 Postgres

Postgres 唯一 JSON 数组聚合值

转换数组类型

在 PostgreSQL 中返回插入的行

处理 sqlalchemy 断开连接的更好方法

更详细地解释 JOIN 与 LEFT JOIN 和 WHERE 条件性能建议

python pip install psycopg2 安装错误

判断materialized视图的上次刷新时间

错误:prepared statement "S_1" already exists

Postgres 中有多少表分区太多了?

Array Push的 Postgres 数组追加和数组长度

PostgreSQL 字符串(255) 限制 - Rails、Ruby 和 Heroku

GRANT SELECT 特权使用一个语句对所有序列

性能调优:为布尔列创建索引