2011-04-09 83 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; 
    }); 
});