當你有一個內存消耗問題與一個排隊系統,你是100%肯定,所有排隊的項目已被從商店移除,不坐成一個異常/錯誤隊列,那麼最可能的原因是排隊率遠高於出隊率。
的Redis使用通用內存分配器(jemalloc,ptmalloc,tcmalloc,等...)。這些分配器不一定會將內存送回系統。當一些內存被釋放時,分配器往往會保留它(重用它以用於未來的分配)。當許多小對象被隨機分配時,尤其如此,Redis通常是這種情況。
結果是在給定時間點的內存消耗的峯值會導致Redis的累積內存,並保持它。這個內存不會丟失,如果出現內存消耗的另一個峯值,它將被重用。但從系統角度來看,內存仍然分配給Redis。對於一個排隊系統,如果你排列物品的速度比你能夠將它們排出的速度快,你將在內存消耗上達到這樣的高峯。
我的建議是儀器應用程序以定期的時間間隔獲取和記錄隊列長度,以檢查隊列中項目數量的演變(並識別峯值)。
更新時間:
我已經測試了一些東西與KUE瞭解什麼是存儲在Redis的。實際上,數據結構非常複雜(字符串,集合,zsets和散列)。如果你看看Redis的,你會發現以下內容:
q:job:nnn (hash, job definition and properties)
q:search:object:nnn (set, metaphone tokens associated to job nnn)
q:search:word:XXXXX (set, reverse index to support job full-text indexing)
q:jobs:inactive (zset, all the unprocessed jobs)
q:jobs:X:inactive (zset, all the unprocessed jobs of job type X)
q:jobs:active (zset, all the on-going jobs)
q:jobs:X:active (zset, all the on-going jobs of job type X)
q:jobs:complete (zset, all the completed jobs)
q:jobs:X:complete (zset, all the completed jobs of job type X)
q:jobs:failed (zset, all the failed jobs)
q:jobs:X:failed (zset, all the failed jobs of job type X)
q:jobs:delayed (zset, all the delayed jobs)
q:jobs:X:delayed (zset, all the delayed jobs of job type X)
q:job:types (set, all the job types)
q:jobs (zset, all the jobs)
q:stats:work-time (string, work time statistic)
q:ids (string, job id sequence)
我不知道CoffeeScript的好,所以我嘗試使用普通的舊JavaScript來重現問題:
var kue = require('kue'),
jobs = kue.createQueue();
jobs.process('email', function(job,done) {
console.log('Processing email '+JSON.stringify(job))
done();
});
function create_email(i) {
var j = jobs.create('email', {
title: 'This is email '+i
, to: 'didier'
, template: 'Bla bla bla'
});
j.on('complete', function() {
console.log('complete email job #%d', j.id);
j.remove(function(err){
if (err) throw err;
console.log('removed completed job #%d', j.id);
});
});
j.save();
}
for (i=0; i<5; ++i)
{
create_email(i);
}
kue.app.listen(8080);
我跑這代碼,檢查什麼留在Redis的處理後:
redis 127.0.0.1:6379> keys *
1) "q:ids"
2) "q:jobs:complete"
3) "q:jobs:email:complete"
4) "q:stats:work-time"
5) "q:job:types"
redis 127.0.0.1:6379> zrange q:jobs:complete 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
因此,似乎已完成作業被保存在問:工作:完全和q:工作:X:完全不顧工作已被刪除。我建議你在你自己的Redis實例中檢查這些zset的基數。
我的解釋是這些zset的管理髮生在之後發出'completed'事件。所以這些作業被正確刪除,但他們的id被插入到那些zsets中。
解決方法是避免依賴每個作業事件,而是使用每個隊列事件來刪除作業。例如,下面的修改是可以做到:
// added this
jobs.on('job complete', function(id) {
console.log('Job complete '+id)
kue.Job.get(id, function(err, job) {
if (err) return;
job.remove(function(err){
if (err) throw err;
console.log('removed completed job #%d', job.id);
});
});
});
// updated that
function create_email(i) {
var j = jobs.create('email', {
title: 'This is email '+i
, to: 'didier'
, template: 'Bla bla bla'
});
j.save();
}
固定程序後,在Redis的內容要好得多:
redis 127.0.0.1:6379> keys *
1) "q:stats:work-time"
2) "q:ids"
3) "q:job:types"
你或許可以使用類似的策略,從Coffescript。
喜迪迪埃 - 感謝這個答案,但我不認爲它標識問題。我應該提到隊列長度爲零。可以很容易地監控這個KUE庫隊列狀態 - 它包括一個內置的管理服務器和前端。我將編輯我的問題以添加此信息。 – mainsocial 2012-01-17 00:01:57
我已經相應地更新了我的答案。 – 2012-01-17 17:31:25
迪迪埃 - 感謝您的調查。我獨立地得出了很多相同的結論。事實上,我能夠修復kue代碼中的一些錯誤。我已將修復程序簽入我的fork並提出了請求。 – mainsocial 2012-01-27 23:54:57