Redis队列+;PythonRQ:防止高内存使用的正确模式?

Redis队列+;PythonRQ:防止高内存使用的正确模式?,python,heroku,asynchronous,redis,redistogo,Python,Heroku,Asynchronous,Redis,Redistogo,我们目前正在使用Redis来支持Heroku托管的Python应用程序 我们将Redis与pythonrq一起纯粹用作任务队列,以提供一些时间密集型任务的延迟执行。一个任务正在从PostgreSQL数据库中检索一些数据,并将结果写回数据库——因此Redis实例中根本不会保存任何有价值的数据。我们注意到,根据执行的作业数量,Redis正在消耗越来越多的内存(增长速度约为10 MB/小时)。CLI上的FLUSHDB命令将修复此问题(将其降至约700kB的已用RAM),直到RAM再次满为止 根据我们的

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

我们将Redis与pythonrq一起纯粹用作任务队列,以提供一些时间密集型任务的延迟执行。一个任务正在从PostgreSQL数据库中检索一些数据,并将结果写回数据库——因此Redis实例中根本不会保存任何有价值的数据。我们注意到,根据执行的作业数量,Redis正在消耗越来越多的内存(增长速度约为10 MB/小时)。CLI上的FLUSHDB命令将修复此问题(将其降至约700kB的已用RAM),直到RAM再次满为止

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

  • 为了在稳定的内存量下完成任务,我们必须采取哪些不同的措施
  • RAM消耗来自哪里
  • 我可以关掉持久性吗
  • 从文档中我知道,500秒TTL意味着密钥随后“过期”,但实际上并没有被删除。此时键是否仍在消耗内存?我能改变这种行为吗
  • 它是否与失败的队列有关(它显然没有一个TTL附加到作业,这意味着(我认为)这些任务将永远保留)
  • 只是好奇:当使用RQ纯粹作为队列时,Redis DB中保存了什么?它是实际的可执行代码,还是只是对要执行的函数所在位置的引用
很抱歉问了这么多无聊的问题,但我对排队这一话题还不太熟悉,在研究了2天多之后,我已经到了不知道下一步该怎么做的地步。 谢谢
KH

又玩了两天,我发现了问题所在。我想与大家分享这一点,以及一些有用的工具:

核心问题

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

其他操作

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

  • 我们将任务结果的TTL设置为0,调用
    enqueue\u call([…]result\u TTL=0)
  • 我们定义了一个定制的异常处理程序--
    黑洞
    ——来获取所有异常并返回False。这可以防止Redis将任务移动到失败队列中,在该队列中它仍将使用一点内存。例外情况会事先通过电子邮件发送给我们,以便跟踪
沿途有用的工具:

我们刚刚使用了
rediscli

  • redis cli info|grep used_memory_human
    -->显示当前内存使用情况。比较任务执行前后内存占用的理想方法
  • redis cli键“*”
    -->显示存在的所有当前键。这一概述使我认识到,有些任务即使本应被删除,也不会被删除(如上所述,它们因未点击错误而崩溃,因此未被删除)
  • redis cli监视器
    -->显示redis中发生的事情的实时概览。这帮助我发现来回移动的物体太大了
  • redis cli debug object
    -->显示密钥值的转储
  • redis cli hgetall
    -->显示了更可读的密钥值转储(对于将redis纯粹用作任务队列的特定用例尤其有用,因为任务似乎是由python rq以这种格式创建的)
此外,我可以回答我在上面发布的一些问题:

从文档中我知道,500秒TTL意味着密钥随后“过期”,但并未真正删除。此时密钥是否仍在消耗内存?我能否以某种方式更改此行为

事实上,它们被删除了,正如文档所暗示的那样

它是否与失败的队列有关(它显然没有一个TTL附加到作业,这意味着(我认为)这些任务将永远保留)

令人惊讶的是,Redis本身崩溃的作业并没有移动到失败队列,它们只是被“放弃”,这意味着这些值仍然存在,但RQ并不像处理失败作业那样关心它

相关文档

  • Redis命令:
  • python rq中的“黑洞”异常处理程序和请求\u ttl:
如果您使用的是中的“黑洞”异常处理程序,还应在其中添加
作业。取消()

def black_hole(job, *exc_info):
    # Delete the job hash on redis, otherwise it will stay on the queue forever
    job.cancel()
    return False

对我来说,一件不太明显的事情是RQ作业同时具有“description”和“data”属性。如果未指定,则将描述设置为数据的字符串表示形式,这在我的情况下是不必要的冗长。将描述显式设置为简短摘要节省了我的开销

enqueue(func, longdata, description='short job summary')