PostgreSQL和SELECT FOR UPDATE LIMIT

时间:2013-02-25 19:58:50

标签: postgresql transactions locking

请你解释一下这种奇怪的行为:

我有这个存储过程告诉我行是否被锁定

CREATE OR REPLACE FUNCTION tg_availablega_is_unlocked(availablega_id integer)
  RETURNS boolean AS
$BODY$
DECLARE
  is_locked     boolean = FALSE;
BEGIN
  BEGIN
    PERFORM id FROM tg_availablega WHERE id = availablega_id
    FOR UPDATE NOWAIT;
  EXCEPTION 
    WHEN lock_not_available THEN
            is_locked := TRUE;
  END;
  RETURN not is_locked;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

如果我开始执行并执行此操作:

SELECT "tg_availablega"."id",
       "tg_availablega"."isactive",
       "tg_availablega"."schedule",
       "tg_availablega"."zone_tg_id"
FROM "tg_availablega"
WHERE (tg_availablega_is_unlocked("tg_availablega"."id")
       AND "tg_availablega"."zone_tg_id" = 1
       AND "tg_availablega"."isactive" = TRUE
       AND "tg_availablega"."schedule" = 20)
LIMIT 100
FOR
UPDATE;

它锁定并返回100行。如果我在其他事务中同时执行相同的操作,它会锁定并返回不同的100行。如果行总数为101,则第一次执行返回100行,第二次执行仅返回1行。

但是如果我添加ORDER BY子句

SELECT "tg_availablega"."id",
       "tg_availablega"."isactive",
       "tg_availablega"."schedule",
       "tg_availablega"."zone_tg_id"
FROM "tg_availablega"
WHERE (tg_availablega_is_unlocked("tg_availablega"."id")
       AND "tg_availablega"."zone_tg_id" = 1
       AND "tg_availablega"."isactive" = TRUE
       AND "tg_availablega"."schedule" = 20)
***ORDER BY "tg_availablega"."id"***
LIMIT 100
FOR
UPDATE;
然后第一个事务返回100个锁定行,第二个事务返回NO ROWS

为什么?

1 个答案:

答案 0 :(得分:1)

问题是函数tg_availablega_is_unlocked确实锁定了它检查的行。没有顺序,Postgres不会访问所有行 - 因此函数不会被调用所有行。我想你的意思是:

select * from (
SELECT "tg_availablega"."id",
   "tg_availablega"."isactive",
   "tg_availablega"."schedule",
   "tg_availablega"."zone_tg_id"
FROM "tg_availablega"
 WHERE "tg_availablega"."zone_tg_id" = 1
   AND "tg_availablega"."isactive" = TRUE
   AND "tg_availablega"."schedule" = 20)
ORDER BY "tg_availablega"."id"
) a
where tg_availablega_is_unlocked(id)
limit 100