在一个会话中,我尝试锁定“users”表中的多行,并获取用户 $persons = array(0 =>'John', 1 => 'Alan', 2 => 'Ninja');
$fruits = array(0 =>'apple', 1 => 'mango', 2 => 'banana', 3 => 'kiwi');
$str = "[person1] eats [fruit1]";
$str2 = "These [fruit2] belongs to [person3] and his friends";
function replaceFromArr($str) {
global $persons, $fruits;
$matches = [];
preg_match_all("/\[([^\]]*)\]/", $str, $matches);
//var_dump($matches[1]);
foreach($matches[1] as $matched) {
// echo "$matched <br>";
$substr = substr($matched,0,6);
if($substr == "person") {
// extract index and fetch from persons array
$index = substr($matched,6);
$replace = $persons[$index-1];
$str = str_replace("[$matched]",$replace,$str);
}
else {
// extract index and fetch from fruits array
$index = substr($matched,5);
$replace = $fruits[$index-1];
$str = str_replace("[$matched]",$replace,$str);
}
}
return $str;
}
$newStr = replaceFromArr($str);
echo "--- $newStr ---<br>";
$newStr = replaceFromArr($str2);
echo "--- $newStr ---<br>";
的“status”列。
WHERE id = 2
当此事务正在运行时,我在另一个会话中运行此查询:
do $$
declare user_status int;
begin
WITH t(id, status) AS(
SELECT id, status FROM users WHERE id in( 2,4,7,6) order by id FOR UPDATE
)
SELECT status FROM t WHERE id = 2 INTO user_status;
-- just run transaction for a while
FOR i in 1..2000000000 loop
end loop;
end;
$$ language plpgsql
我预计行UPDATE users SET some_col = some_col WHERE id = 6;
应该被第一个事务锁定,但事实并非如此,因为第二个会话中的WHERE id = 6
立即运行(不等待第一个会话中的结束事务)。
我被误解了什么?
P.S。
如果在第一次交易中,而不是使用CTE:
UPDATE
然后锁定按预期工作。
答案 0 :(得分:1)
我认为这是由于PL / pgSQL的$followedids = rtrim($followedids, ',');
在幕后工作的方式。
docs中提到了明显的根本原因:
如果在游标的查询中使用了锁定子句,则实际只有行 光标取出或踩过将被锁定。
...并且此效果将流向查询中的任何CTE,其中只有在外部查询需要行时才会获取(因此锁定)行。
这很容易证明。首先,一些设置:
SELECT INTO
此查询将按预期锁定所有行:
CREATE TABLE t (x INT);
INSERT INTO t VALUES (1),(2),(3);
但是,这会产生您观察到的行为,仅锁定第一条记录:
WITH cte AS (SELECT x FROM t WHERE x IN (1,2,3) FOR UPDATE)
SELECT x FROM cte WHERE x = 1
我的猜测是PL / pgSQL中的DECLARE c CURSOR FOR
WITH cte AS (SELECT x FROM t WHERE x IN (1,2,3) FOR UPDATE)
SELECT x FROM cte WHERE x = 1;
FETCH NEXT FROM c;
以相同的方式运行;如docs中所述,如果查询返回多行,则第一行被分配给目标变量,其余的被忽略(因此不需要它来获取多个记录)。
但是,将 锁定SELECT INTO
中的所有行:
t
请注意SELECT INTO STRICT
的使用。如果查询返回多行,DO $$
DECLARE i INT;
BEGIN
WITH cte AS (SELECT x FROM t WHERE x IN (1,2,3) FOR UPDATE)
SELECT x FROM cte WHERE x = 1 INTO STRICT i;
END
$$ LANGUAGE plpgsql;
关键字将导致抛出错误。但是为了发现第二行被返回,PL / pgSQL必须尝试获取它,这会导致CTE查询获取剩余的记录(在过程中将它们锁定)。
所有这些在PL / pgSQL函数中大部分时间都不是问题,因为您通常从STRICT
查询中获取所有记录,并将它们传递给对谁实际更新它们。另一方面,如果您想要查询的副作用但对其输出不感兴趣,那么(如您所见)您可以使用FOR UPDATE
,它将运行查询以完成,但会丢弃结果。
在极少数情况下,您需要(即锁定)所有行,而只返回其中的一部分,您可能需要编写自己的loop。