2013-04-10 61 views
0

我有一個簡單的過程,我需要處理表的記錄,理想情況下運行多個進程的實例,而不處理相同的記錄。我已經與MySQL這樣做的方式是相當普遍的(雖然我認爲令牌場更加黑客攻擊的):PostgreSQL表上並行進程

添加幾個字段的表:

CREATE TABLE records (
    id INTEGER PRIMARY KEY AUTO_INCREMENT, 
    ...actual fields... 

    processed_at DATETIME DEFAULT NULL, 
    process_token TEXT DEFAULT NULL 
); 

然後一個簡單的處理腳本:

process_salt = md5(rand()) # or something like a process id 

def get_record(): 
    token = md5(microtime + process_salt) 
    db.exec("UPDATE records SET process_token = ? 
      WHERE processed_at IS NULL LIMIT 1", token) 
    return db.exec("SELECT * FROM records WHERE token = ?", token) 

while (row = get_record()) is valid: 
    # ...do processing on row... 

    db.exec("UPDATE records SET processed_at = NOW(), token = NULL 
      WHERE id = ?", row.id) 

我在一個使用PostgreSQL數據庫的系統中實現這樣一個過程。我知道Pg可以被認爲比MySQL更成熟,關於MVCC鎖定問題 - 我可以在Pg中使用行鎖定還是其他功能,而不是令牌字段?

回答

1

這種做法將與PostgreSQL的工作,但它會往往是相當低效的,因爲你要更新的每一行兩次 - 每次更新需要交易,兩次提交。通過使用commit_delay並可能禁用synchronous_commit,可以稍微緩解此問題的成本,但是,除非在存儲子系統上有非易失性回寫緩存,否則它的速度仍然不會很快。

更重要的是,由於您正在進行第一次更新,因此無法區分正在工作的工作人員和已經崩潰的工人之間的區別。如果所有工作人員都在本地計算機上,偶爾可以掃描丟失的PID,但這很麻煩,容易出現競爭情況,您可以將該標記設置爲工作人員的進程標識,更不用說使用pid重用的問題了。

我會建議你採用,旨在解決這些問題,喜歡的ActiveMQ,RabbitMQ的,ZeroMQ等PGQ也可能是顯著關心的真正排隊的解決方案。

在事務性關係數據庫中執行隊列處理應該是很容易,但在實踐中,做得很好並且得到正確的結果是很荒唐的。當仔細檢查時,大多數看起來合理的「解決方案」實際上是對所有工作進行序列化(因此,只有許多隊列工作人員中的任何一個正在做任何事情)。

+0

是的,這種方法總是覺得像用錘子敲釘子 - 我一直想看看ZeroMQ很長一段時間,所以我會走這條路。感謝您的洞察! – Ross 2013-04-11 20:08:57

0

您可以使用SELECT ... FOR UPDATE NOWAIT這將獲得行上的排他鎖,或者報告錯誤(如果它已被鎖定)。