保护CTE中的关键资源表(WITH子句)

时间:2017-06-23 23:02:45

标签: postgresql concurrency transactions common-table-expression

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

交易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范围分隔的事务?

1 个答案:

答案 0 :(得分:2)

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

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

要获得这样的排序保证,您可以使用带有OFFSET 0的FROM中子查询,或者您可以使some_stuff中的查询直接依赖于lock_op来确保它&# 39;首先评估。

就个人而言,如果你能减少MVCC行流失,我可以保留它的原样,可能是SELECT ... FOR UPDATE而不是UPDATE

对于其他读者来说,重要的是要注意这张海报并不是假设在某个语句中以某种方式做事情会使它们以原子方式发生,不受并发效应的影响。那个假设将绝对错误。 CTE不是魔术并发修复酱。

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

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

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