2012-03-22 27 views
0

我有一個名爲ticket的表,它有一個名爲number的字段和一個名爲client的外鍵,它需要像自動字段一樣工作(每個新記錄增加1),除了客戶端鏈需要能夠指定起始號碼。這不是一個獨特的領域,因爲多個客戶無疑會使用相同的號碼(例如從1001開始)。在我的應用我獲取該行最高number,並使用該number+ 1使下記錄的number。這一切都發生在單個事務中(取得和保存新記錄)。在高負荷情況下,我是否真的不必擔心ticket會得到不正確的(重複的)number,或者交易是否會保護這種可能性? (注意:我正在使用PostgreSQL 9.x)如果在事務中完成,我可以信任手動排序Postgres中的整數字段嗎?

+0

如果事務回滾,該怎麼辦? – 2012-03-22 17:14:03

+0

@mattb - 這並不意味着數據庫中什麼都不會改變,因此下一個'number'會再次可用?我不打算在多次交易中用同一位重試操作。 – orokusaki 2012-03-22 17:28:56

+0

你有一個唯一的索引(客戶端,號碼),如果沒有,爲什麼不呢? – 2012-03-22 18:42:50

回答

1

沒有鎖定整個表上每個插入/更新,沒有。事務處理PostgreSQL的方式意味着作爲併發事務結果出現的新行不會相互衝突;這就是會發生什麼。

您需要確保更新實際上導致相同的行發生衝突。你基本上需要實現類似於PostgreSQL本地序列所使用的機制。

我會做的是在您的client列引用的表格中添加另一列,以表示您將使用的序列的last_val。因此,每一筆交易看起來有點像這樣:

BEGIN; 

SET TRANSACTION SERIALIZABLE; 

UPDATE clients 
    SET footable_last_val = footable_last_val + 1 
    WHERE clients.id = :client_id; 

INSERT INTO footable(somecol, client_id, number) 
    VALUES (:somevalue, 
      :client_id, 
      (SELECT footable_last_val 
      FROM clients 
      WHERE clients.id = :client_id)); 

COMMIT; 

這樣第一次更新到客戶表失敗由於到達前插入一個版本衝突。

+0

版本衝突?你是否假設交易以可序列化模式運行? (而不是默認的「讀取提交」)。 – 2012-03-22 18:42:23

+0

@Daniel:嗯,是的,我假設。我被我使用的框架寵壞了;這是默認設置的。 – SingleNegationElimination 2012-03-22 18:45:49

+0

@TokenMacGuy - 你使用Django嗎?這是我使用的框架。 – orokusaki 2012-03-26 14:39:22

1

你不必擔心重複的號碼。

典型問題的方案是:事務T1讀取N,並創建一個新行N + 1。但是在T1提交之前,另一個事務T2將N視爲此客戶端的最大值,並創建另一個N + 1 =>衝突的新行。

有很多方法可以避免這種情況;這裏是一個簡單的plpgsql代碼,它實現了一種方法,假設一個唯一的索引(客戶端,數字)。解決方案是讓插入同時運行,但在發生唯一索引違規時,重新刷新值直到它被接受爲止(它不是一個繁忙的循環,因爲併發插入被阻塞,直到其他事務提交)

do 
$$ 
begin 
loop 
    BEGIN 
    -- client number is assumed to be 1234 for the sake of simplicity 
    insert into the_table(client,number) 
     select 1234, 1+coalesce(max(number),0) from the_table where client=1234; 
    exit; 
    EXCEPTION 
    when unique_violation then -- nothing (keep looping) 
    END; 
end loop; 
end$$; 

該示例與PG文檔中的UPSERT implementation有點相似。 將客戶端ID作爲輸入,它很容易轉換爲plpgsql函數。

相關問題