Redis Queue + python-rq:正确的模式,以防止高内存使用?

kar*_*sch 11 python asynchronous heroku redis redistogo

我们目前正在使用我们的Heroku托管的Python应用程序使用Redis.

我们将Redis与python-rq一起用作任务队列,以延迟执行一些时间紧迫的任务.任务是从PostgreSQL数据库中检索一些数据并将结果写回给它 - 因此Redis实例中根本没有保存有价值的数据.我们注意到,根据执行的作业数量,Redis正在消耗越来越多的内存(增长@ 10 MB /小时).CLI上的FLUSHDB命令修复此问题(将其降至约700kB的RAM),直到RAM再次满为止.

根据我们的(未更改的标准)设置,作业结果保持500秒.随着时间的推移,一些作业当然会失败,并且它们会被移动到失败的队列中.

  • 我们必须采取哪些不同的方式才能使用稳定的RAM来完成任务?
  • RAM消耗来自哪里?
  • 我可以完全关闭持久性吗?
  • 从文档我知道500秒TTL意味着一个密钥然后"过期",但没有真正删除.此时密钥是否还消耗内存?我可以以某种方式改变这种行为吗?
  • 它是否与失败的队列有关(显然没有TTL附加到作业,这意味着(我认为)这些是永久保存的)?
  • 只是好奇:当将RQ纯粹用作队列时,Redis DB中保存了什么?它是实际的可执行代码还是只能找到要执行的函数的位置?

很抱歉这些漂亮的noobish问题,但我对排队的话题不熟悉,经过2天以上的研究,我已经达到了一个我不知道下一步要做的事情.谢谢,KH

kar*_*sch 14

经过两天的游戏,我发现了问题.我想与您分享这些以及有用的工具:

核心问题

实际问题是我们忽略了在将对象保存到PostgreSQL数据库之前将其转换为字符串.如果没有这个强制转换,字符串表示最终会出现在DB中(由于__str__()相应对象的功能正好返回我们想要的表示); 然而,对Redis来说,整个对象都被传递了.将其传递给Redis后,相关任务因UnpickleError异常而崩溃.这消耗了5 MB的RAM,在崩溃后没有释放.

其他行动

为了进一步减少内存占用,我们实现了以下补充操作(请注意,我们将所有内容保存到单独的数据库中,因此我们的应用程序中根本不会使用Redis保存的结果):

  • 我们通过调用将任务结果的TTL设置为0 enqueue_call([...] result_ttl=0)
  • 我们定义了一个自定义异常处理程序 - black_hole- 获取所有异常并返回False.这可以防止Redis将任务移动到仍然使用一点内存的失败队列.事先通过电子邮件向我们发送例外情况以跟踪它们.

一路上有用的工具:

我们刚刚合作过redis-cli.

  • redis-cli info | grep used_memory_human - >显示当前的内存使用情况.理想的是在执行任务之前和之后比较内存占用.
  • redis-cli keys '*' - >显示存在的所有当前键.这个概述让我明白了一些任务即使应该被删除也不会被删除(如上所述,它们因UnpickleError而崩溃,因此没有被删除).
  • redis-cli monitor - >显示Redis中发生的事情的实时概述.这有助于我发现来回移动的物体太大了.
  • redis-cli debug object <key> - >显示密钥值的转储.
  • redis-cli hgetall <key> - >显示了一个更可读的密钥值转储(特别适用于将Redis纯粹用作任务队列的特定用例,因为看起来这些任务是由python-rq以这种格式创建的.

此外,我可以回答上面发布的一些问题:

从文档我知道500秒TTL意味着一个密钥然后"过期",但没有真正删除.此时密钥是否还消耗内存?我可以以某种方式改变这种行为吗?

实际上,它们被删除了,就像文档所暗示的那样.

它是否与失败的队列有关(显然没有TTL附加到作业,这意味着(我认为)这些是永久保存的)?

令人惊讶的是,Redis本身崩溃的工作没有转移到失败的队列,他们只是"放弃",这意味着价值仍然存在,但RQ并不关心它与失败工作的正常方式.

相关文件