5

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

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

然而,仅仅抓取缓存的对象(在第 1 行中)会导致服务器上的内存使用量出现巨大的峰值 - 有时超过 100MB - 并且即使在返回值并渲染模板之后内存也不会释放。

我怎样才能不让记忆像这样膨胀?此外,我在获取值后尝试显式删除对象,但即使这样也不会释放内存峰值。

任何帮助是极大的赞赏。

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

我决定实现我自己的索引表,在其中存储键和它们的腌制值。现在,我没有get()在字典上使用,而是使用:

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

并取消价值。这似乎解决了内存问题,因为我不再将巨大的对象加载到内存中。它向页面添加了另一个小查询,仅此而已。似乎是完美的解决方案......现在。

4

3 回答 3

4

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

无论如何,100MB 内存包含所有数据 + 哈希索引 + 杂项。高架; 前几天我注意到自己在退出 Python 进程之前,很多时候内存都没有被释放(我从 Python 解释器中填充了几个 gig 的内存,加载了一个巨大的 json 对象.. :));如果有人对此有解决方案会很有趣..

更新:用很少的内存缓存

只有 512MB 内存的选项是:

  • 使用 redis,看看这里http://redis.io/topics/memory-optimization(但我怀疑 512MB 还不够,甚至优化)
  • 使用具有更多内存的单独机器(或集群,因为 memcached 和 redis 都支持分片)来保留缓存
  • 使用数据库缓存后端,速度更慢但内存消耗更少,因为它将所有内容都保存在磁盘上
  • 使用文件系统缓存(尽管我没有看到比数据库缓存更喜欢它的意义)

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

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

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

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')
于 2012-11-02T01:40:51.810 回答
0

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

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

因此,由内存峰值引起的膨胀会一直持续到最后。解决方案:

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

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

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

代码可能看起来像cache.get('{prefix}search_term')那时。

编辑:

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

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

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

于 2012-11-05T08:38:35.713 回答