【问题标题】:Protect critical resource tables in a CTE (WITH clause)保护 CTE 中的关键资源表(WITH 子句)
【发布时间】:2017-06-23 23:02:45
【问题描述】:

我正在使用我更新的中间表,以确保不能在不能同时访问的关键表上处理其他并发操作。

事务 1

BEGIN
UPDATE locktable
/* Do some stuff */
...
COMMIT

并发事务2

BEGIN
Update locktable
/* Do some other stuff */
...
COMMIT

这样我可以确定事务 1 和事务 2 是原子的。

出于简化和性能原因,我将代码更改为 WITH 子句语句。 我想知道我是否可以以与 CTE 相同的方式保证操作原子性。

CTE 简化示例:

事务 1

WITH 

lock_op AS (
UPDATE locktable
...
RETURNING id),

some_stuff AS
(
/* Do insert and update operations with RETURNING clause*/
...
)

SELECT * 
FROM some_stuff
WHERE EXISTS (SELECT 1 FROM lock_op)

并发事务2

WITH 

lock_op AS (
UPDATE locktable
...
RETURNING id),

other_stuff AS
(
/* Do insert and update operations with RETURNING clause*/
...
)

SELECT * 
FROM other_stuff
WHERE EXISTS (SELECT 1 FROM lock_op)

基本上,我想知道“SELECT 1 FROM lock_op”是否在来自 some_stuff 和 other_stuff 的任何 INSERT 和 UPDATE 之前启动,因此,是否暂时保护了由 WITH 范围分隔的事务的关键数据?

【问题讨论】:

    标签: postgresql concurrency transactions common-table-expression


    【解决方案1】:

    您在这里没有相同的订购保证。没有承诺 lock_op 的查询将在 some_stuff 之前执行。

    否则它是相当好的。行锁在lock_op 中获取,并一直保持到包含 CTE 的隐式事务(如果您不使用显式开始/提交)被提交。

    要获得这样的排序保证,您可以使用带有OFFSET 0 的子查询,或者您可以使some_stuff 中的查询直接依赖于lock_op,以确保首先对其进行评估。

    就我个人而言,如果可以减少 MVCC 行流失,我可能会使用 SELECT ... FOR UPDATE 而不是 UPDATE


    对于其他读者,重要的是要注意,这张海报并不是假设以某种方式在单个语句中执行某事会使它们自动发生,不受并发影响。这种假设将是绝对错误的。 CTE 不是神奇的并发修复酱。

    您必须使用行或表锁定或(小心并理解)使用SERIALIZABLE 隔离+重试循环。

    最简单的方法是在进行更改的事务中LOCK TABLE ... IN EXCLUSIVE MODE。这允许并发读取,但不允许写入。

    对于更细粒度的锁定,请使用带有SELECT ... FOR UPDATE 的子查询或 CTE 术语。

    【讨论】:

    • 感谢这个非常明确的答案。我想我会选择一个混合解决方案来实现两个不同的请求:1/一个用于锁定 lock_op 表,2/另一个用于 CTE。两者都在一个事务中。我的 CTE 包含许多出于一致性原因需要全局原子的命令,并且(如果我理解正确的话)SELECT FOR UPDATE 将只保证单个操作不会被并发访问/更改。
    • 如果您需要全局隔离,请考虑使用建议锁。它应该比SELECT ... FOR UPDATE-ing 一些 1-row lock table 便宜得多。
    • 感谢您的提示。我不知道咨询锁。它可能满足我的要求,因为我需要隔离的所有操作都链接到我可以提供给 pg_advisory_lock 函数的给定单个 id。
    • 只是纠正自己。咨询锁不符合我的要求,因为它们使用的全局 ID 会阻止引用同一 ID 的任何其他请求,尽管我只想阻止特定用户的特定操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-15
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    • 2021-07-13
    • 2015-09-05
    • 2015-08-10
    相关资源
    最近更新 更多