Postgresql

順序查詢會導致並發事務嗎?

  • October 30, 2015

我正在使用 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 的相關問題的衍生:

引用自:https://dba.stackexchange.com/questions/119441