5

Djangoビューでキャッシュしてアクセスする約60,000個のキーを持つ辞書オブジェクトがあります。ビューは、次のように辞書で検索語を検索する基本的な検索機能を提供します。

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

ただし、キャッシュされたオブジェクト(1行目)を取得するだけで、サーバーのメモリ使用量が大幅に増加し(場合によっては、100 MB以上)、値が返され、テンプレートがレンダリングされた後でも、メモリは解放されません。

このようにメモリがジャッキアップしないようにするにはどうすればよいですか?また、値を取得した後でオブジェクトを明示的に削除しようとしましたが、それでもメモリスパイクは解放されません。

どんな助けでも大歓迎です。

更新:最終的に実装したソリューション

キーとそのピクルス値を格納する独自のインデックステーブルを実装することにしました。get()今、辞書で使用する代わりに、私は以下を使用します:

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

値を選択解除します。巨大なオブジェクトをメモリにロードしなくなったので、これでメモリの問題が解決されたようです。ページに別の小さなクエリを追加しますが、それだけです。完璧な解決策のようです...今のところ。

4

3 に答える 3

4

..メモリpython側に巨大なオブジェクトをロードする代わりに、redismemcachedなどのキャッシュに適切なサービスを使用するのはどうですか?このようにして、辞書がさらに大きくなった場合に、追加のマシンでスケーリングすることもできます。

とにかく、100MBのメモリにはすべてのデータ+ハッシュインデックス+その他が含まれています。オーバーヘッド; 先日、Pythonプロセスを終了するまで、メモリの割り当てが解除されないことに気づきました(Pythonインタープリターから数ギガのメモリをいっぱいにして、巨大なjsonオブジェクトをロードしました。.:)); 誰かがその解決策を持っていれば面白いでしょう..

更新:メモリが非常に少ないキャッシュ

512MBのRAMのみを使用するオプションは次のとおりです。

  • redisを使用し、こちらをご覧くださいhttp://redis.io/topics/memory-optimization(ただし、最適化すら、512MBでは不十分だと思います)
  • キャッシュを維持するために、より多くのRAMを備えた別のマシン(またはmemcachedとredisの両方がシャーディングをサポートしているためのクラスター)を使用します
  • データベースキャッシュバックエンドを使用します。ディスクにすべてを保存するため、速度は大幅に低下しますが、メモリ消費量は少なくなります。
  • ファイルシステムキャッシュを使用します(データベースキャッシュよりもこれを優先する意味はわかりませんが)

そして、後者の2つのケースでは、オブジェクトを分割してみてください。そうすれば、キャッシュからメガバイトのオブジェクトを一度に取得することはありません。

更新:複数のキャッシュキーにまたがる怠惰なdict

キャッシュされたdictを次のようなものに置き換えることができます。このようにして、通常の辞書と同じように処理を続けることができますが、データは本当に必要な場合にのみキャッシュからロードされます。

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()内部メモリプールのみを増やすために使用されます。実行中のアプリケーションでは、このメモリプールは要求されたブロックで雑然としています。システムにメモリを返す唯一の可能性は、プールの最後の部分でブロックが使用されていない場合です(単純なアプリケーションでも数千のオブジェクトを割り当ておよび割り当て解除するため、これがかなりのサイズになる可能性はほとんどありません)。

したがって、メモリスパイクによって引き起こされた膨満感は最後まで残ります。ソリューション:

  • 一時的なオブジェクトが原因である場合でも、メモリ使用量を最適化することでスパイクを回避します(例:コンテンツ全体を一度に読み取るのではなく、ファイルを1行ずつ処理する)
  • キャッシュを別のプロセスに配置します(最初の回答で提案されているように、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 に答える