2011-04-09 84 views
1

我的情況......數據庫支持工作隊列

我有一組工人已安排定期運行,每一個不同的時間間隔的,想找到一個很好的實現來管理它們的執行。

示例:假設我有一名工人前往商店並每週購買一次我的牛奶。我想存儲這個工作,它的配置在一個MySQL表中。但是,它看起來像一個壞主意輪詢表(每秒?),看看哪些作業已準備好放入執行管道。

我所有的工作人員都是用javascript編寫的,所以我使用node.js來執行,並且使用beanstalkd作爲管道。

如果新作業(即調度工作人員在給定時間運行)正在異步創建,並且需要持久存儲作業結果和配置,我該如何避免輪詢表格?

謝謝!

回答

2

我同意這看起來不太優雅,但鑑於方式,電腦工作東西 * *某處將不得不做的某種投票,以找出哪些作業時執行。因此,我們來看看您的一些選項:

  1. 輪詢數據庫表。這根本不是一個壞主意 - 如果你將作業存儲在MySQL中,這可能是最簡單的選擇。每秒一個查詢的速率是沒有的 - 嘗試一下,你會發現你的系統甚至沒有感覺到它。

    一些想法,以幫助你達到這可能是每秒數百次查詢,或者只是保持系統資源的要求下:

    • 創建第二個表,「job_pending」,在那裏你把需要的就業機會在接下來的X秒/分鐘/小時內執行。
    • 長時間在所有作業的大表上運行查詢一次,然後填充查詢每個較短時間的小表。
    • 刪除從小表中執行的作業,以使其保持較小。
    • 在'execute_time'(或任何你所說的)列上使用索引。
  2. 如果您有進一步擴大,保持主工作表在數據庫中,並使用第二個,較小的表,我建議,只是把該表在RAM:無論是作爲在數據庫引擎內存表,或者在你的程序中某種類型的隊列中。如果您有太多的時間間隔,則以極短的間隔查詢隊列 - 這將導致一些極端的使用情況,導致此處出現任何性能問題。

    該選項的主要問題是您必須跟蹤內存中未執行的作業,例如,由於系統崩潰 - 爲您編寫更多的代碼...

  3. 爲一堆作業中的每一個(例如,需要在下一分鐘執行的所有作業)創建一個線程,並調用thread.sleep(millis_until_execution_time )(或其他,我不熟悉node.js)。

    此選項與no有相同的問題。 2 - 你必須跟蹤崩潰恢復的執行情況。這也是最浪費的imo - 每個睡眠工作線程仍然需要系統資源。

當然可能還有其他選擇 - 我希望其他人可以回答更多的想法。

只要意識到每秒輪詢數據庫根本就不是一個壞主意。這是最直接的方式imo(記住KISS),以這種速度你不應該有性能問題,以避免過早的優化。

+0

很好的點。謝謝! – Josh 2011-04-10 03:39:50

+0

我同意KISS說投票是好的。 OTOH,通知者/觀察者沒有任何投票。當有事情需要發生時,所有訂戶都會收到通知。唯一的網絡流量是keepalive或heartbeats,可以在一些守護進程/系統/軟件中儘可能少地發生。 – squarism 2012-03-13 18:58:49

1

爲什麼不在保存到數據庫的node.js中有一個Job對象。

var Job = { 
    id: long, 
    task: String, 
    configuration: JSON, 
    dueDate: Date, 
    finished: bit 
}; 

我建議你只在RAM中存儲該ID並將所有其他Job數據留在數據庫中。當你的超時功能最終運行時,只需要知道.id就可以獲得其他數據。

var job = createJob(...); // create from async data somewhere. 
job.save(); // save the job. 
var id = job.id // only store the id in RAM 
// ask the job to be run in the future. 
setTimeout(Date.now - job.dueDate, function() { 
    // load the job when you want to run it 
    db.load(id, function(job) { 
     // run it. 
     run(job); 
     // mark as finished 
     job.finished = true; 
     // save your finished = true state 
     job.save(); 
    }); 
}); 
// remove job from RAM now. 
job = null; 

如果服務器崩潰過你所查詢的是有[finished=false]所有作業,它們加載到RAM中,然後再次啓動一個定時器。

如果出現任何錯誤,你應該能夠乾淨利落重啓像這樣:

db.find("job", { finished: false }, function(jobs) { 
    each(jobs, function(job) { 
     var id = job.id; 
     setTimeout(Date.now - job.dueDate, function() { 
      // load the job when you want to run it 
      db.load(id, function(job) { 
       // run it. 
       run(job); 
       // mark as finished 
       job.finished = true; 
       // save your finished = true state 
       job.save(); 
      }); 
     }); 
     job = null; 
    }); 
});