我正在使用我更新的中间表,以确保不能在不能同时访问的关键表上处理其他并发操作。
交易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范围分隔的事务?
答案 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术语。