2013-11-21 40 views
4

我在Postgres中使用pg_try_advisory_lock()Postgres pg_try_advisory_lock阻止所有記錄

接下來兩個查詢在table1鎖定多個記錄:

1)

SELECT a.id 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
WHERE 
    table2.id = 1 
    AND 
    pg_try_advisory_lock('table1'::regclass::integer, a.id) 
LIMIT 1; 

SELECT a.id 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
WHERE table2.id = 1 

返回一個記錄。

2)

SELECT a.id 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
JOIN table3 c ON b.table2_id = c.id 
WHERE 
    table3.id = 1 
    AND 
    pg_try_advisory_lock('table1'::regclass::integer, a.id) 
LIMIT 1; 

但我需要pg_try_advisory_lock()只鎖定一個記錄。

怎麼了?

UPD

但奇怪的是,當我運行下面的查詢

SELECT a.id 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
WHERE 
    pg_try_advisory_lock('table1'::regclass::integer, a.id) 
LIMIT 1; 

Postgres的鎖只有一行。那麼,Postgres掃描第一行然後停止?我不明白:它應該掃描所有行,然後將結果限制爲一行,否則?

回答

5

您在被掃描的整個集合(作爲where子句中發生的過濾的一部分)中每行調用一次pg_try_advisory_lock(),而您只希望它在查詢返回的table1中每行調用一次。

你可以嘗試使用子查詢或CTE來代替:

with rows as (
SELECT a.id 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
WHERE table2.id = 1 
) 
select rows.* 
from rows 
where pg_try_advisory_lock('table1'::regclass::integer, rows.id); 

但不要依賴於預期要麼一定工作:Postgres的應該被誘惑把它改寫你的初始查詢的樣子。

另一種可能是這樣的,因爲聲明的select部分評估在查詢很晚:

with rows as (
SELECT a.id, 
     pg_try_advisory_lock('table1'::regclass::integer, a.id) as locked 
FROM table1 a 
JOIN table2 b ON a.table1_id = b.id 
WHERE table2.id = 1 
) 
select rows.id 
from rows 
where rows.locked; 

在實踐中真正的問題是pg_try_advisory_lock()是東西,你會在app土地或常找在一個函數中,而不是像你在做的查詢。說到這一點,取決於你在做什麼,你確定你不應該使用select … for update


關於你提到的更新:

Postgres的掃描則非常第一行停止?

是的。由於limit 1,它會找到一個匹配並立即停止。但是,可能發生的情況是,它不會根據您的查詢以相同順序評估where子句。 SQL不保證a <> 0 and b/a > c中的a <> 0部分得到首先評估。適用於你的情況,它不能保證在之後得到諮詢鎖a的行與b結合。

+0

我不需要select ... for update,因爲另一個腳本必須找到另一個記錄。 – fdoo4un

+0

我仍然認爲你應該鎖定在app-land中,而不是在sql-land中。 –

+0

CTE工作良好。謝謝你的建議! 此外我更新了問題。見plz。 – fdoo4un