2012-01-12 66 views
1

我具有可以簡單地表示爲一個優先級隊列表:SQL優先級隊列操作 - 任何陷阱?

CREATE TABLE test (
    id int PRIMARY KEY, 
    priority int, 
    status int 
) 

其中priority爲優先級(第一最低)和status是物品狀態(0表示行檢索)。任務是檢索具有最高優先級的元素和重置它的狀態,所以代碼是這樣的(僞):

try = 0; 
while(try++ < max_tries) { 
    result = query("SELECT id FROM test WHERE status=0 ORDER BY priority LIMIT 0,1"); 
    if(result not empty) { 
     /* found a row, try to update */ 
     result2 = query("UPDATE test SET status=1 WHERE id={result[id]} AND status=0"); 
     if(affected_rows(result2) > 0) { 
     /* if update worked fine, we can use this ID */ 
     return result[id]; 
     } 
     /* otherwise try again */ 
} 
return NULL; 

此代碼應該運行(與適當的修改,就像不同的限制語法)的任何SQL數據庫(目前的需求是Mysql,Oracle,SQL Server和DB2,但可能更多)。據我所知,所有必需的數據庫都支持「受影響的行」API進行更新。這種方法有什麼潛在的問題或陷阱? SELECT ... FOR UPDATE應該在上面的語句中使用,如果是,爲什麼?

+0

您是否打算使用任何交易? – 2012-01-13 00:32:35

回答

1

列出的SELECT/UPDATE組合似乎解決了UPDATE的WHERE子句的concrrency問題。然而,如果可以接受的話,我會傾向於一個存儲過程:根據sql風格,存儲過程可以通過原子「UPDATE ... RETURNING ID INTO ...」來消除併發問題。

至於美國,如果他們是這樣的:

  • 0 「沒關係檢索」
  • 1 「進行中」
  • 2 「完成」

則狀態充當鎖。如果進程在狀態從0更改爲1之後死亡,但在作業實際處理之前會死機:在清理作業運行之前,記錄可以保持鎖定和未處理狀態。另一種方法是爲鎖使用單獨的時間戳列。然後查詢包括一個過濾器「WHERE now() - timeout> NVL(lockTimestamp,when of time)」,鎖定是通過將​​lockTimestamp設置爲now()而不是依賴於設置status = 1來完成的。這樣,在合理的等待(超時)之後,鎖可以自動失效,並且該項再次可用以由下一個處理器拾取。無需清理工作。

任何一個低劣優先工作可能無法獲得的飢餓機會?

是否需要添加額外的訂購?如果這些項目具有相同的優先級,那麼首先處理哪些項目(fifo,lifo)是否重要?我想應該注意的是,如果這個抓取例程有多個實例在運行,那麼可能會同時處理多個隊列項目,並且處理順序不一定是順序的(process1獲取item1,process2獲取item2,process2完成item2,process1完成item1,...),所以希望這是好的,否則鎖定需要檢查沒有其他任何進程。

我也沒有特別看到需要一個SELECT ... FOR UPDATE。

+0

我認爲RETURNING id INTO是僅限於Oracle的,不是嗎?我需要一些可以在最少修改的情況下在大多數SQL DB上運行的東西。 – StasM 2012-01-13 09:06:50

+0

@StasM你是對的,似乎不是無處不在。甲骨文和Postgresql有它,除此之外我不知道。我猜存儲過程的使用可能也不是一個選項,但我真的很喜歡這種情況下存儲過程的想法,以便能夠將api從實現中分離出來。該應用程序只是說「getNext」,存儲過程使用它自己的語言功能指出要做什麼。 – Glenn 2012-01-13 12:47:44

+0

DB2的後續版本可以'SELECT FROM NEW TABLE(UPDATE ...)' - 但是,這並不存在於iSeries版本上(我認爲只有zSeries,LUW)。另外,根據預期作業在系統中停留的時間長短,清理作業可能無法確定要解鎖哪些實例。 – 2012-01-13 17:04:23