Djangoのキャッシュフレームワークを使った場合のキーと値の取り扱い

Djangoフレームワークには、データをキャッシュする仕組みを抽象化、共通化したキャッシュフレームワークが含まれています。

Django's cache framework | Django ドキュメント | Django

どのようなキーと値が保存されるのか

キャッシュフレームワークAPIで、どのようなキーと値がミドルウェアなどのバックエンドに保存されるのか確認してみます。

キャッシュに値を入れる

今回はDjango 5.0とredisバックエンドで試してみます。ローカル環境ではRedisが起動している想定。

myproject/settings.py:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://",
    }
}

この設定でDjangomanage.py shell からキャッシュを保存します。

>>> from django.core.cache import cache
>>> cache.set("my-cache-key", "キャッシュの値")
>>> cache.get("my-cache-key")  # キャッシュから取り出せるか確認
'キャッシュの値'

キーと値を見てみる

保存できたら、 redis-cli で確認してみます。

$ redis-cli  # シェルからredis-cliを起動
127.0.0.1:6379> KEYS *
1) ":1:my-cache-key"
127.0.0.1:6379> GET :1:my-cache-key
"\x80\x05\x95\x19\x00\x00\x00\x00\x00\x00\x00\x8c\x15\xe3\x82\xad\xe3\x83\xa3\xe3\x83\x83\xe3\x82\xb7\xe3\x83\xa5\xe3\x81\xae\xe5\x80\xa4\x94."

まず、 KEYS <pattern> コマンドでpatternに * を指定し、すべてのキー一覧を取得しています。

:1:my-cache-key という値がキーになっています。

settings.pyでキー生成の関数を設定していないので、デフォルトのDjangoのキー生成関数が使われています。 実装はこの辺です。 https://github.com/django/django/blob/617bcf611f3daa796e4054ba041089ece30a32fc/django/core/cache/backends/base.py#L40

return "%s:%s:%s" % (key_prefix, version, key)
  • key_prefix は、settings.CACHESの各キャッシュの設定で KEY_PREFIX キーにて変更できます。デフォルトは空文字列です。
  • version settings.CACHESの各キャッシュの設定で VERSION キーで指定できます。

https://github.com/django/django/blob/617bcf611f3daa796e4054ba041089ece30a32fc/django/core/cache/backends/base.py#L82

GET コマンドで取得した値はバイナリ値になっています。これはDjango側のRedisバックエンドの中で、保存する値をpickleモジュールでシリアライズしているからです。

https://github.com/django/django/blob/617bcf611f3daa796e4054ba041089ece30a32fc/django/core/cache/backends/redis.py#L21

どのように値を保持するかは、キャッシュバックエンドごとで異なるので、シリアライズ・デシリアライズ処理を行うかどうかは、バックエンドクラスの実装次第です。

Djangoのキャッシュフレームワークを通してRedisに保存した値を、他のアプリなどから読み込んで使いたい場合、Pickleフォーマットだと扱いづらいかもしれません。その場合は自分でdumps, loadsメソッドを持ったクラスを実装するか、 json モジュールなどを指定することもできます(この記事では手順は説明しません)

キー生成の関数を変更してみる

キーを生成する関数を変更するには、settings.pyのCACHESで KEY_FUNCTION を設定します。

myproject/utils.py

def my_key_func(key, key_prefix, version):
    return "spam:{}".format(key)

myproject/settings.py:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://",
        "KEY_FUNCTION": "myproject.utils.my_key_func",  # 文字列指定だと実行時にインポートして利用される
    }
}

この設定の状態でキャッシュを manage.py shell から追加してみます。

>>> from django.core.cache import cache
>>> cache.set("my-cache-key-2", "キャッシュの値")
>>> cache.get("my-cache-key-2")
'キャッシュの値'

redis-cliにてキーを見てみましょう。

$ redis-cli
127.0.0.1:6379> keys *
1) "spam:my-cache-key-2"

キー文字列が変わったことを確認できました。

ありそうな質問

キャッシュキーの一覧を取得したいですが、うまくいきません。どうすればよいですか?

Djangoのキャッシュフレームワークでは、キー一覧を返す仕組みを持っていません。この記事の例ではRedisの KEYS コマンドを使用しています。

キャッシュを保存しておくバックエンドのミドルウェアがキーの一覧を返す仕組みを持たない場合もあります。キャッシュキーを検索したい要件がある場合は、キャッシュバックエンドの選定に気をつけるとよいでしょう。

cacheのすべてのkeyの取得の方法。

キャッシュを使っているのに本番環境が遅いです。ローカル環境では問題ないのになぜですか?

ネットワーク経由で外部キャッシュサーバーを使用している場合は、通信のレイテンシがあります。リクエスト内で何度もキャッシュを読み書きすると、遅くなる場合があります。複数のキーを指定してまとめて取得、まとめて更新する方法を使うと改善する可能性があります。

Djangoのcacheフレームワークで複数の値をまとめて取得、更新する - 偏った言語信者の垂れ流し