加载导致内存峰值的字典对象

时间:2012-11-02 01:30:54

标签: python django

我有一个字典对象,有大约60,000个密钥,我在Django视图中缓存和访问。该视图提供了基本的搜索功能,我在字典中查找搜索词,如下所示:

projects_map = cache.get('projects_map')
projects_map.get('search term')

然而,只是抓住缓存的对象(在第1行)会导致服务器上的内存使用量大幅增加 - 有时超过100MB - 即使在返回值并呈现模板后内存也不会释放。 / p>

如何保持内存不会像这样抬起?此外,我尝试在获取值后显式删除对象,但即使这样也不会释放内存峰值。

非常感谢任何帮助。

更新:我最终实施的解决方案

我决定实现自己的索引表,在其中存储密钥及其pi​​ckle值。现在,我使用:

而不是在字典上使用get()

ProjectsIndex.objects.get(index_key=<search term>)

并取消该值。这似乎解决了内存问题,因为我不再将巨型对象加载到内存中。它为页面添加了另一个小查询,但这就是它。现在似乎是一个完美的解决方案。

3 个答案:

答案 0 :(得分:4)

..如何使用一些适当的缓存服务,如redismemcached而不是在内存python端加载巨大的对象?这样,如果字典增长更多,你甚至可以扩展额外的机器。

无论如何,100MB内存包含所有数据+哈希索引+ misc。高架;前几天我注意到很多次内存都没有被释放,直到你退出Python进程(我从Python解释器填充了几个内存,加载了一个巨大的json对象.. :));如果任何人都有解决方案,这将是有趣的..

更新:内存极少的缓存

只有512MB RAM的选项是:

  • 使用redis,看看http://redis.io/topics/memory-optimization(但我怀疑512MB还不够,甚至优化)
  • 使用单独的计算机(或集群,因为memcached和redis都支持分片)以及更多ram来保留缓存
  • 使用database cache backend,速度要慢得多但内存消耗更少,因为它可以保存磁盘上的所有内容
  • 使用filesystem cache(虽然我没有看到优先考虑数据库缓存)

并且,在后两种情况下,尝试拆分对象,这样就不会一次从缓存中检索数兆字节的对象。

更新:跨越多个缓存键的懒惰字典

你可以用这样的东西替换你的缓存字典;这样,您可以像使用普通字典一样继续处理它,但只有在您真正需要时才会从缓存加载数据。

from django.core.cache import cache
from UserDict import DictMixin

class LazyCachedDict(DictMixin):
    def __init__(self, key_prefix):
        self.key_prefix = key_prefix

    def __getitem__(self, name):
        return cache.get('%s:%s' % (self.key_prefix, name))

    def __setitem__(self, name, value):
        return cache.set('%s:%s' % (self.key_prefix, name), value)

    def __delitem__(self, name):
        return cache.delete('%s:%s' % (self.key_prefix, name))

    def has_key(self, name):
        return cache.has_key(name)

    def keys():
        ## Just fill the gap, as the cache object doesn't provide
        ## a method to list cache keys..
        return []

然后替换它:

projects_map = cache.get('projects_map')
projects_map.get('search term')

使用:

projects_map = LazyCachedDict('projects_map')
projects_map.get('search term')

答案 1 :(得分:0)

我不知道Windows是如何工作的,但在Linux中,一个进程实际上无法将内存返回给系统。这是因为进程地址空间是连续的,并且唯一可用的增加内存的系统调用是brk(),它只会增加一个指针,该指针标记进程可用的最后一个地址。

应用程序使用(malloc等)的所有分配器都在用户空间中定义为库。它们在字节块级别上运行,并使用brk()仅增加内部内存池。在正在运行的应用程序中,此内存池与请求的块混杂在一起。将内存返回给系统的唯一可能性是当池的最后一部分没有使用块时(这不太可能,因为即使是简单的应用程序也会分配和释放数千个对象)。

因此记忆飙升引起的膨胀将一直持续到最后。解决方案:

  • 通过优化内存使用来避免峰值,即使是由临时对象引起的(例如:逐行处理文件而不是一次读取整个内容)
  • 将缓存放在另一个进程中(memcached,如第一个答案所示)
  • 使用序列化字典(gdbm)或与进程'私有内存(mmap,共享内存)分离的其他存储

答案 2 :(得分:0)

如果特定键上的get是您执行的唯一操作,为什么不将所有键分别保存在缓存中?这样,所有条目都将以单独的文件结束,django将能够访问它们quickly

当然,更新会更加痛苦,但你可以很好地抽象它。我能想到的第一件事就是一些缓存键前缀。

代码看起来像cache.get('{prefix}search_term')

修改

我们在这里试图解决错误的问题。您不需要缓存。数据得到更新,而不是转储(5分钟左右)。

您需要创建一个包含所有条目的数据库表。

如果您无法从您的设置访问任何数据库服务器,请尝试使用sqlite。它是基于文件的,应该很好地满足您的目的。