在node.js中使用redis支持的“kue”库 – 为什么我的redis内存使用量不断增加?

在node.js应用程序中,我使用了由redis支持的kue队列库。 当一个工作完成,我把它从队列中删除。 在夜间运行大约70,000个工作后,redis内存使用量大约为30MB。 数据库中还有18个失败的作业,队列长度目前为零 – 作业处理速度比排队更快。 Redis没有被其他方式使用。

任何想法,即使我删除完成的工作,为什么redis内存使用量不断增加? 咖啡代码:

gaemodel.update = (params) -> job = jobs.create "gaemodel-update", params job.attempts 2 job.save() job.on "complete", -> job.remove (err) -> throw err if err console.log 'completed job #%d', job.id 

当你有一个排队系统的内存消耗问题,并且你100%肯定所有排队的项目已经被从商店中删除,而不是坐在一个exception/错误队列中,那么最可能的原因是排队费率远高于出院率。

Redis使用通用的内存分配器(jemalloc,ptmalloc,tcmalloc等)。 这些分配器不一定把内存送回系统。 当释放一些内存时,分配器往往会保留它(为了将来的分配而重用它)。 当许多小对象被随机分配时,尤其如此,Redis通常是这种情况。

其结果是在给定时间点内存消耗的高峰将导致Redis积累内存并保持它。 这个内存不会丢失,如果出现内存消耗的另一个峰值,它将被重用。 但是从系统angular度来看,内存仍然分配给Redis。 对于一个排队系统,如果你排队的项目比你能够出队更快,你将有这样的高峰在内存消耗。

我的build议是让你的应用程序按照定期的时间间隔取出和logging队列长度,以检查队列中项目数量的变化(并确定峰值)。

更新:

我已经用kuetesting了一些东西来了解它在Redis中存储的内容。 实际上,数据结构非常复杂(string,集合,zset和散列)。 如果你看看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:工作:完成和q:工作:X:完成,尽pipe工作已被删除。 我build议你在你自己的Redis实例中检查这些zset的基数。

我的解释是pipe理这些zset发生 “完成”事件发生 。 所以这些作业被正确地删除了,但是他们的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的类似策略。

很高兴看到你解决了你的问题。 在任何情况下,下次Redis出现内存问题时,您的第一个端口应该是“INFO”redis命令。 这个命令会告诉你有价值的信息,比如

记忆

used_memory:3223928 used_memory_human:3.07M used_memory_rss:1916928 used_memory_peak:3512536 used_memory_peak_human:3.35M used_memory_lua:37888 mem_fragmentation_ratio:0.59

要么

KEYSPACE

DB0:键= 282,期满= 27,avg_ttl = 11335089640

在任何特定时刻,对于了解内存状态和密钥空间非常方便。

事实上,问题出在旧版本的节点上。 升级到0.6.x链解决了内存消耗问题。