Postgresql
順序查詢會導致並發事務嗎?
我正在使用 PostgreSQL 9.4.4 和預設配置。我使用 JDBC 連接連續執行多個查詢。只有一個 Java 執行緒正在使用該連接,並且在執行新查詢之前獲取前一個查詢的結果集。下面是一個簡化的草圖:
while(true) { PreparedStatement stmt = //... ResultSet rs = stmt.executeQuery(); //process result }
導致問題的查詢呼叫以下儲存過程:
CREATE OR REPLACE FUNCTION delete_top_message(_queue_id BIGINT , _source_client_id BIGINT) RETURNS TEXT AS $$ DECLARE rv TEXT; BEGIN DELETE FROM message m USING ( SELECT * FROM message WHERE message.queue_id = _queue_id AND (message.receiver_client_id = _source_client_id OR message.receiver_client_id = -1) ORDER BY insertion_time ASC LIMIT 1) AS tmp WHERE m.id = tmp.id RETURNING m.content INTO rv; IF (SELECT rv IS NULL) THEN RAISE 'No message available.'; END IF; RETURN rv; END; $$ LANGUAGE plpgsql;
我正在觀察第 13.2.1 節中描述的行為http://www.postgresql.org/docs/9.1/static/transaction-iso.html。閱讀送出的隔離級別。
發生的情況是觸發了過程中的異常,儘管當我在異常發生時中止一切並手動呼叫過程時,不會引發異常。我進一步注意到,如果事務已經在消息表上持有行獨占鎖,那麼就會引發異常。
另外,如果我添加
LOCK TABLE message IN ROW EXCLUSIVE MODE;
在程序開始時,問題就消失了。
因此,我懷疑在上述循環中,查詢在其事務實際送出之前返回結果,因此下一個事務與它發生衝突。這會發生嗎?注意我沒有在 PostgreSQL 文件中找到解釋這一點的東西。另外我沒有啟用非同步送出。
在標題中回答您的問題:不。
同一會話(是否相同事務)中的連續查詢永遠不會並發。並且同一會話中一次只能有一個事務。那樣的話並發問題是不可能的,那些只會發生在多個會話的事務之間。
您的錯誤消息的一個可能原因可能是您的功能出現故障,可以簡化:
CREATE OR REPLACE FUNCTION delete_top_message(_queue_id bigint , _source_client_id bigint) RETURNS SETOF text AS $func$ BEGIN RETURN QUERY DELETE FROM message m USING ( SELECT id FROM message WHERE queue_id = _queue_id AND receiver_client_id IN (_source_client_id, -1) ORDER BY insertion_time LIMIT 1 ) m1 WHERE m.id = m1.id RETURNING m.content; IF NOT FOUND THEN RAISE 'No message available.'; END IF; END $func$ LANGUAGE plpgsql;
您的原始支票:
IF (SELECT rv IS NULL) THEN
可以簡化為:
IF rv IS NULL THEN
但這仍然不好。它在
message.content
為 NULL 時觸發,不僅在未找到任何行時觸發。問題中缺少表定義,所以我們不知道這是否可能。無論哪種方式,特殊變數
FOUND
都是更好的工具。我的審核版本也更簡單更快。RETURN
請注意,plpgsql 函式在函式結束或到達之前不會返回結果。如果需要,我們可以只返回行並在以後取消結果。手冊中有一個範例,這是關於 SO 的相關問題的衍生: