cis*_*cis 9 postgresql cte locking update postgresql-13
在 Postgres 13 中,我有一个经常更新的表。然而,更新查询相当复杂,并且多次使用相同的值。因此,使用 CTE 似乎是一件非常合乎逻辑的事情。
一个简化的示例如下所示:
WITH my_cte AS (
SELECT
my_id,
CASE WHEN my_value1 > 100 THEN 50 ELSE 10 END AS my_addition
FROM my_table
WHERE my_id = $1
)
UPDATE my_table
SET my_value1 = my_table.my_value1 + my_cte.my_addition,
my_value2 = my_table.my_value2 + my_cte.my_addition
FROM my_cte
WHERE my_table.my_id = my_cte.my_id
Run Code Online (Sandbox Code Playgroud)
现在我想知道:如果在SELECT
CTE 和 之间UPDATE
,表被另一个查询更新,my_value1
从而发生变化,那么当发生这种情况时,were 的计算my_addition
就会变得过时且错误,会发生什么UPDATE
。会出现这样的情况吗?或者 Postgres 是否自动设置隐式锁?
如果 Postgres 在这里没有魔法,我需要自己处理它:FOR UPDATE
在SELECT
CTE 中做就足够了吗?
抱歉,如果我没有在这里说清楚:我并不是想“看到”这些并发修改,我想阻止它们,即一旦计算完成SELECT
,没有其他查询可能会修改该行,直到计算UPDATE
完成。
在现实生活中,我在这里嘲笑的内容CASE WHEN my_value1 > 100 THEN 50 ELSE 10 END
大约有 20 行长,我需要它在UPDATE
. 因为我是“不要重复自己”的忠实粉丝,所以我认为 CTE 是正确的选择。或者是否有更好的方法来避免在UPDATE
没有 CTE 的情况下复制和粘贴?
Erw*_*ter 10
Postgres 使用多版本模型(多版本并发控制,MVCC)。
在默认READ COMMITTED
隔离级别下,每个单独的查询实际上都会看到查询开始运行时数据库的快照。如果在中间提交并发事务,后续查询(即使在同一事务中)也可以看到不同的快照。(加上迄今为止在同一笔交易中所做的事情。)
然而,就CTE而言,中的所有子语句都WITH
与外部语句同时执行,它们实际上看到的是数据库的相同快照。为此,所有这些都被视为单个查询。
所以,不,您不需要显式锁定来保持一致性。
由于多种原因,将逻辑封装在函数中可能很方便,但这对并发性没有任何影响。旁白:具有易失性函数的 CTE永远不会被内联。看:
ASELECT
不锁定查询的行。Postgres 允许并发UPDATES
。但UPDATE
锁定目标行。并发事务也尝试写入,必须等到锁定事务完成。
如果您想禁止写入仅在正在进行时选择的行(列)UPDATE
,您可能无论如何都需要锁定(或使用更严格的隔离级别)。也许是FOR UPDATE
锁,或者可能是较弱的锁。这取决于您在问题中明确保留/不提供的细节和要求。
另外(尽管您没有要求这样做),如果多个并发事务可能写入重叠的行(一次多个行),请务必遵守相同、一致的行顺序以避免死锁。这通常需要ORDER BY
在锁定之前。
如果对此有贡献的列ORDER BY
可能会被并发事务更新,您还需要添加NOWAIT
以确保确定。锁定发生在ORDER BY
. 如果并发事务更新其间的一行,Postgres 将等待该事务完成。如果提交,该行现在可能会以不同的方式排序,并且锁定将无序发生,从而重新引入死锁的可能性。这NOWAIT
是确保READ COMMITTED
隔离级别的唯一方法。或者使用更严格的隔离级别。REPEATABLE READ
或者SERIALIZABLE
无论如何都会引发序列化失败。
归档时间: |
|
查看次数: |
3747 次 |
最近记录: |