Skip Navigation
Show nav
Dev Center
  • Get Started
  • ドキュメント
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • ドキュメント
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku のアーキテクチャ
    • Dyno (アプリコンテナ)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • スタック (オペレーティングシステムイメージ)
    • ネットワーキングと DNS
    • プラットフォームポリシー
    • プラットフォームの原則
  • Developer Tools
    • コマンドライン
    • Heroku VS Code Extension
  • デプロイ
    • Git を使用したデプロイ
    • Docker によるデプロイ
    • デプロイ統合
  • 継続的デリバリーとインテグレーション
    • 継続的統合
  • 言語サポート
    • Node.js
      • Working with Node.js
      • Node.js Behavior in Heroku
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails のサポート
      • Bundler の使用
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Python でのバックグランドジョブ
      • Python Behavior in Heroku
      • Django の使用
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Maven の使用
      • Spring Boot の使用
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go の依存関係管理
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • データベースとデータ管理
    • Heroku Postgres
      • Postgres の基礎
      • Postgres スターターガイド
      • Postgres のパフォーマンス
      • Postgres のデータ転送と保持
      • Postgres の可用性
      • Postgres の特別なトピック
      • Migrating to Heroku Postgres
    • Heroku Data For Redis
    • Apache Kafka on Heroku
    • その他のデータストア
  • AI
    • Model Context Protocol
    • Vector Database
    • Heroku Inference
      • Inference Essentials
      • AI Models
      • Inference API
      • Quick Start Guides
    • Working with AI
  • モニタリングとメトリクス
    • ログ記録
  • アプリのパフォーマンス
  • アドオン
    • すべてのアドオン
  • 共同作業
  • セキュリティ
    • アプリのセキュリティ
    • ID と認証
      • シングルサインオン (SSO)
    • Private Space
      • インフラストラクチャネットワーキング
    • コンプライアンス
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Team
    • Heroku Connect (Salesforce 同期)
      • Heroku Connect の管理
      • Heroku Connect のリファレンス
      • Heroku Connect のトラブルシューティング
  • パターンとベストプラクティス
  • Heroku の拡張
    • Platform API
    • アプリの Webhook
    • Heroku Labs
    • アドオンのビルド
      • アドオン開発のタスク
      • アドオン API
      • アドオンのガイドラインと要件
    • CLI プラグインのビルド
    • 開発ビルドパック
    • Dev Center
  • アカウントと請求
  • トラブルシューティングとサポート
  • Salesforce とのインテグレーション
  • 言語サポート
  • Python
  • Django の使用
  • Memcache を使用した Django アプリケーションのスケーリング

This article was contributed by The MemCachier Add-on

MemCachier manages and scales clusters of memcache servers so you can focus on your app. Tell us how much memory you need and get started for free instantly. Add capacity later as you need it.

follow @MemCachier on Twitter

Memcache を使用した Django アプリケーションのスケーリング

日本語 — Switch to English

この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。

最終更新日 2020年08月26日(水)

Table of Contents

  • 前提条件
  • Heroku 用 Django アプリケーションの作成
  • タスクリスト機能の追加
  • キャッシングを Django に追加する
  • Django の統合キャッシングの使用
  • セッションストレージでの Memcache の使用
  • 参考情報と資料

Memcache は、Web アプリとモバイルアプリバックエンドのパフォーマンスとスケーラビリティを改善する技術です。ページの読み込みが遅すぎる場合や、アプリにスケーラビリティの問題がある場合は、Memcache の使用を検討してください。小規模なサイトであっても、Memcache の導入によってページの読み込みを高速化し、将来の変化にアプリを対応させることができます。

このガイドでは、単純な Django 2.1​ アプリケーションを作成して Heroku にデプロイし、Memcache を追加してパフォーマンスのボトルネックを軽減する方法を示します。

Django 2 以降​では Python 2​ がサポートされなくなったため、この記事では Python 3​ を主なターゲットにしています。ただし、これより古いバージョンの Django で Python 2 を使用する場合も、このガイドの内容は有効です。

このガイドで扱うサンプルアプリの動作は こちら​で確認できます。また、 ソースコードを確認​したり、 次の Heroku ボタンを使用してデプロイすることができます。

Heroku にデプロイ

前提条件

このガイドの手順を完了する前に、以下のすべての条件を満たしていることを確認してください。

  • Python の知識がある (Django についても知識があることが理想的です)
  • Heroku ユーザーアカウント (無料ですぐにサインアップ)
  • 「Heroku スターターガイド (Python)​」の手順を理解している
  • Python と Heroku CLI​ がコンピュータにインストールされている

Heroku 用 Django アプリケーションの作成

次のコマンドは、空の Django アプリを作成します。これらのコマンドの詳細な説明は「Python および Django アプリのデプロイ​」にあります。

$ mkdir django_memcache && cd django_memcache
$ python -m venv venv    # For Python 2 use `virtualenv venv`
$ source venv/bin/activate
(venv) $ pip install Django django-heroku gunicorn
(venv) $ django-admin.py startproject django_tasklist .
(venv) $ pip freeze > requirements.txt
(venv) $ python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

Django version 2.0, using settings 'django_tasklist.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

http://localhost:8000​ にアクセスすると “hello, world” ランディングページが表示されます。

Heroku のための Django の設定

Django アプリを Heroku で動作させるには、いくつかの Heroku 固有の設定が必要です。

  1. Procfile​ を追加して、アプリの起動方法を Heroku に指示します。

    $ echo "web: gunicorn django_tasklist.wsgi --log-file -" > Procfile
    
  2. Django アプリを Heroku で動作させるために必要な、主にデータベースの動作と静的ファイルの供給に関する Heroku 固有の設定を追加します。幸いにも、そのすべてを実行する django-heroku​ パッケージがあります。django_tasklist/settings.py​ ファイルの最後に次の行を追加します。

    # Configure Django App for Heroku.
    import django_heroku
    django_heroku.settings(locals())
    

    For more information about these Heroku specific settings see Configuring Django Apps for Heroku. Note: django-heroku only supports Python 3. For Python 2 please follow the instructions in Configuring Django Apps for Heroku.

Heroku へのデプロイ

コードを Heroku にデプロイするには、先に Git リポジトリにコードを追加する必要があります。最初に、.gitignore​ を編集して次の行を追加し、不要なファイルを除外します。

venv
*.pyc
db.sqlite3

次に、Git リポジトリを初期化し、コミットします。

$ git init
$ git add .
$ git commit -m "Empty django app"

ここで、heroku create​ を使用して Heroku アプリを作成します。

$ heroku create
Creating app... done, ⬢ blooming-ridge-97247
https://blooming-ridge-97247.herokuapp.com/ | https://git.heroku.com/blooming-ridge-97247.git

次に、アプリをデプロイします。

$ git push heroku master

最後に、Heroku CLI を使用してブラウザでアプリを表示できます。

$ heroku open

ローカル開発モードと同じ “hello, world” ランディングページが表示されますが、今回は Heroku プラットフォームで実行されている点が異なります。

タスクリスト機能の追加

構築する Django アプリケーションはタスクリストです。リストの表示に加えて、新しいタスクの追加とタスクの削除を実行するアクションがあります。

最初に、Django アプリ mc_tasklist​ を作成します。

(venv) $ python manage.py startapp mc_tasklist

django_tasklist/settings.py​ で、インストール済みアプリのリストに mc_tasklist​ を追加します。

INSTALLED_APPS = (
    'django.contrib.admin',
    # ...
    'mc_tasklist',
)

これで、以下の 4 つの手順でタスクリスト機能を追加できます。

  1. mc_tasklist/models.py​ で単純な Task​ モデルを作成します。

    from django.db import models
    
    class Task(models.Model):
        name = models.TextField()
    

    Use makemigrations and migrate to create a migration for the mc_tasklist app as well as create the mc_tasklist_tasks table locally, along with all other default Django tables:

    (venv) $ python manage.py makemigrations mc_tasklist
    (venv) $ python manage.py migrate
    
  2. django_tasklist/urls.py​ で、add、remove、index の各メソッドのルートを設定します。

    # ...
    from mc_tasklist import views
    urlpatterns = [
        # ...
        path('add', views.add),
        path('remove', views.remove),
        path('', views.index),
    ]
    
  3. mc_tasklist/views.py​ で、対応するビューコントローラーを追加します。

    from django.template.context_processors import csrf
    from django.shortcuts import render_to_response, redirect
    from mc_tasklist.models import Task
    
    def index(request):
        tasks = Task.objects.order_by("id")
        c = {'tasks': tasks}
        c.update(csrf(request))
        return render_to_response('index.html', c)
    
    def add(request):
        item = Task(name=request.POST["name"])
        item.save()
        return redirect("/")
    
    def remove(request):
        item = Task.objects.get(id=request.POST["id"])
        if item:
            item.delete()
        return redirect("/")
    
  4. mc_tasklist/templates/index.html​ で、表示コード付きのテンプレートを作成します。

    <!DOCTYPE html>
    <head>
      <meta charset="utf-8">
      <title>MemCachier Django tutorial</title>
      <!-- Fonts -->
      <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css"
            rel='stylesheet' type='text/css' />
      <!-- Bootstrap CSS -->
      <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
            rel="stylesheet" />
    </head>
    
    <body>
      <div class="container">
        <!-- New Task Card -->
        <div class="card">
          <div class="card-body">
            <h5 class="card-title">New Task</h5>
    
            <form action="add" method="POST">
              {% csrf_token %}
              <div class="form-group">
                <input type="text" class="form-control" placeholder="Task Name"
                       name="name" required>
              </div>
              <button type="submit" class="btn btn-default">
                <i class="fa fa-plus"></i> Add Task
              </button>
            </form>
          </div>
        </div>
    
        <!-- Current Tasks -->
        {% if tasks %}
        <div class="card">
          <div class="card-body">
            <h5 class="card-title">Current Tasks</h5>
    
            <table class="table table-striped">
              {% for task in tasks %}
              <tr>
                <!-- Task Name -->
                <td class="table-text">{{ task.name }}</td>
                <!-- Delete Button -->
                <td>
                  <form action="remove" method="POST">
                    {% csrf_token %}
                    <input type="hidden" name="id" value="{{ task.id }}">
                    <button type="submit" class="btn btn-danger">
                      <i class="fa fa-trash"></i> Delete
                    </button>
                  </form>
                </td>
              </tr>
              {% endfor %}
            </table>
          </div>
        </div>
        {% endif %}
      </div>
    
      <!-- Bootstrap related JavaScript -->
      <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
    </body>
    </html>
    

    Django will automatically check each apps templates folder for templates.

python manage.py runserver​ を実行し、http://localhost:8000​ に再度アクセスし、いくつかのタスクを追加および削除して、基本的なタスクリストアプリの動作を確認します。

ローカルでは、SQLite データベースを使用してタスクリストを保存します。Heroku では、データベースをプロビジョニングする必要があります。

$ heroku addons:create heroku-postgresql:hobby-dev

ここで、タスクリストを Heroku にデプロイします。

$ git add .
$ git commit -m "Task list functionality"
$ git push heroku master

最後に、Heroku 上でデータベースを移行して mc_tasklist_tasks​ テーブルを作成し、Heroku アプリを再起動します。

$ heroku run python manage.py migrate
$ heroku restart

heroku open​ でアプリを表示し、いくつかのタスクを追加して、Heroku 上でもアプリが動作することを確認します。

キャッシングを Django に追加する

Memcache はインメモリの分散キャッシュです。そのプライマリ API は、SET(key, value)​ と GET(key)​ の 2 つの操作で構成されます。 Memcache は、複数のサーバーに分散しているが操作は一定の時間に実行されるハッシュマップ (または辞書) のようなものです。

Memcache の最も一般的な用途は、コストの高いデータベースクエリや HTML レンダリングの結果をキャッシュし、これらの高コスト操作を繰り返す必要をなくすことです。

Memcache のプロビジョニング

Django で Memcache を使用するには、まず実際の Memcached キャッシュをプロビジョニングする必要があります。これは、MemCachier アドオン​から無料で簡単に入手できます。

$ heroku addons:create memcachier:dev

新しい Memcache インスタンスが自動的にプロビジョニングされ、MemCachier の資格情報を含む一連の環境設定​が公開されます。

Django での MemCachier の設定

Django 1.11 以降では、Django ネイティブの pylibmc​ バックエンドを使用できます。これよりも古いバージョンの Django の場合、django-pylibmc​ をインストールする必要があります。詳細は、この記事の 古いバージョン を参照してください。

django_tasklist/settings.py​ の最後に次の内容を追加して、キャッシュを設定します。

def get_cache():
  import os
  try:
    servers = os.environ['MEMCACHIER_SERVERS']
    username = os.environ['MEMCACHIER_USERNAME']
    password = os.environ['MEMCACHIER_PASSWORD']
    return {
      'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        # TIMEOUT is not the connection timeout! It's the default expiration
        # timeout that should be applied to keys! Setting it to `None`
        # disables expiration.
        'TIMEOUT': None,
        'LOCATION': servers,
        'OPTIONS': {
          'binary': True,
          'username': username,
          'password': password,
          'behaviors': {
            # Enable faster IO
            'no_block': True,
            'tcp_nodelay': True,
            # Keep connection alive
            'tcp_keepalive': True,
            # Timeout settings
            'connect_timeout': 2000, # ms
            'send_timeout': 750 * 1000, # us
            'receive_timeout': 750 * 1000, # us
            '_poll_timeout': 2000, # ms
            # Better failover
            'ketama': True,
            'remove_failed': 1,
            'retry_timeout': 2,
            'dead_timeout': 30,
          }
        }
      }
    }
  except:
    return {
      'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
      }
    }

CACHES = get_cache()

これにより、開発と本番の両環境でキャッシュが設定されます。 MEMCACHIER_*​ 環境変数が存在する場合、キャッシュは pylibmc​ で設定され、MemCachier に接続します。一方、MEMCACHIER_*​ 環境変数が存在しない (つまり開発モードの) 場合、Django の単純なインメモリキャッシュが代わりに使用されます。

依存関係のインストール

pylibmc​ をインストールするには、C ライブラリ libmemcached​ が必要です。Heroku では libmemcached​ がインストール済みのため、この点は心配ありません。ただし、pylibmc​ をローカルでテストする場合はインストールが必要です (プロセスはプラットフォームにより異なります)。

Ubuntu の場合:

$ sudo apt-get install libmemcached-dev

OS X の場合:

$ brew install libmemcached

Libmemcached は、SASL 認証のサポートを有効または無効にして ビルドできます。SASL 認証は、Heroku が提供するユーザー名と パスワードを使用して MemCachier サーバーで認証するために クライアントが使用するメカニズムです。使用する libmemcached のビルドが SASL をサポート していることを確認する必要があります。これを行うには、 ガイド​に従ってください。

次に、pylibmc​ Python モジュールをインストールします。

(venv) $ pip install pylibmc==1.5.2

pylibmc のインストールが libmemcached​ を見つけられない場合、 LIBMEMCACHED=/opt/local pip install pylibmc​ のように指定することが必要な場合があります (libmemcached​ が /opt/local​ にインストールされている場合の例)。必要な場合、/opt/local​ は正しいディレクトリに 置き換えてください。

新しい依存関係で requirements.txt​ ファイルを更新します (pylibmc​ がローカルにインストールされていない場合、pylibmc==1.5.2​ を requirements.txt​ に追加するだけです)。

$ pip freeze > requirements.txt
$ cat requirements.txt
...
pylibmc==1.5.2

最後に、これらの変更をコミットしてデプロイします。

$ git commit -am "Connecting to memcache."
$ git push heroku master

Memcache 設定の確認

次に進む前に、memcache が正しく設定されていることを確認します。

これを行うには、Django シェルを実行します。ローカルマシンで python manage.py shell を実行し、Heroku で heroku run python manage.py shell を実行します。and in Heroku run​ クイックテストを実行して、キャッシュが正しく設定されていることを確認します。

>>> from django.core.cache import cache
>>> cache.get("foo")
>>> cache.set("foo", "bar")
>>> cache.get("foo")
'bar'

Ctrl-d​ で終了します。2 番目の get​ コマンドの後、foo​ がキャッシュから取得されると bar​ が画面に出力されるはずです。bar​ が出力されない場合、キャッシュは正しく設定されていません。

コストの高いデータベースクエリをキャッシュする

Memcache を使用して、コストの高いデータベースクエリをキャッシュすることはよくあります。この単純な例にはコストの高いクエリは含まれませんが、学習のために、すべてのタスクをデータベースから取得するのはコストの高い操作であると仮定します。

mc_tasklist/views.py​ のタスクリストデータベースクエリコードは、次のように、最初にキャッシュをチェックするように変更できます。

# ...
from django.core.cache import cache
import time

TASKS_KEY = "tasks.all"

def index(request):
    tasks = cache.get(TASKS_KEY)
    if not tasks:
        time.sleep(2)  # simulate a slow query.
        tasks = Task.objects.order_by("id")
        cache.set(TASKS_KEY, tasks)
    c = {'tasks': tasks}
    c.update(csrf(request))
    return render_to_response('index.html', c)
# ...

上記のコードでは、最初にキャッシュをチェックして、tasks.all​ キーがキャッシュに存在するかどうかを確認します。 存在しない場合、データベースクエリが実行され、キャッシュが更新されます。 これにより、後続のページロードでデータベースクエリを実行する必要がなくなります。 time.sleep(2)​ は、低速なクエリをシミュレートするために存在しているだけです。

この新しい機能をデプロイしてテストします。

$ git commit -am 'Add caching with MemCachier'
$ git push heroku master
$ heroku open

キャッシュ内で何が起きているかを確認するために、MemCachier のダッシュボードを開きます。

$ heroku addons:open memcachier

タスクリストを最初に読み込むと、get miss​ と set​ コマンドが増加しているはずです。それ以降は、タスクリストを再読み込みするたびに get hit​ が増加するはずです (ダッシュボードの統計を更新してください)。

キャッシュは機能していますが、まだ大きな問題があります。新しいタスクを追加して結果を確認します。すると、新しいタスクが現在のタスクリストに反映されていません。新しいタスクがデータベースに作成されましたが、アプリで表示されているのはキャッシュから取得した古いタスクリストです。

Memcache の最新状態の維持

キャッシュが古くなる問題に対処する手法は数多くあります。

  1. 有効期限:​ キャッシュが古くなるのを確実に防ぐ最も簡単な方法は、有効期限の設定です。cache.set​ メソッドには、オプションの 3 番目の引数を指定できます。これは、キャッシュキーをキャッシュに保持する秒数です。 このオプションを指定しない場合、デフォルトの TIMEOUT​ 値 (settings.py​ 内) が代わりに使用されます。

    You could modify the cache.set method to look like this:

    cache.set(TASKS_KEY, tasks, 5)
    

    But this functionality only works when it is known for how long the cached value is valid. In our case however, the cache gets stale upon user interaction (add, remove a task).

  2. キャッシュされた値の削除:​ 簡単な戦略として挙げられるのは、キャッシュが古くなっていることを把握したら tasks.all​ キーを無効にする、具体的には add​ ビューと remove​ ビューを変更して tasks.all​ キーを削除することです。

    # ...
    def add(request):
        item = Task(name=request.POST["name"])
        item.save()
        cache.delete(TASKS_KEY)
        return redirect("/")
    
    def remove(request):
        item = Task.objects.get(id=request.POST["id"])
        if item:
            item.delete()
            cache.delete(TASKS_KEY)
        return redirect("/")
    
  3. キーベースの有効期限:​ 古くなったデータを無効化する別の手法は、キーを変更することです。

    # ...
    import random
    import string
    
    def _hash(size=16, chars=string.ascii_letters + string.digits):
        return ''.join(random.choice(chars) for _ in range(size))
    
    def _new_tasks_key():
        return 'tasks.all.' + _hash()
    
    TASKS_KEY = _new_tasks_key()
    
    # ...
    
    def add(request):
        item = Task(name=request.POST["name"])
        item.save()
        global TASKS_KEY
        TASKS_KEY = _new_tasks_key()
        return redirect("/")
    
    def remove(request):
        item = Task.objects.get(id=request.POST["id"])
        if item:
            item.delete()
            global TASKS_KEY
            TASKS_KEY = _new_tasks_key()
        return redirect("/")
    

    The upside of key based expiration is that you do not have to interact with the cache to expire the value. The LRU eviction of Memcache will clean out the old keys eventually.

  4. キャッシュの更新:​ キーを無効化する代わりに、値を更新して新しいタスクリストを反映させることもできます。

    # ...
    def add(request):
        item = Task(name=request.POST["name"])
        item.save()
        cache.set(TASKS_KEY, Task.objects.order_by("id"))
        return redirect("/")
    
    def remove(request):
        item = Task.objects.get(id=request.POST["id"])
        if item:
            item.delete()
            cache.set(TASKS_KEY, Task.objects.order_by("id"))
        return redirect("/")
    

    値を削除するのではなく更新するようにすることで、最初のページロード時にデータベースにアクセスする必要がなくなります。

2 番目、3 番目、または 4 番目の手法を使用することで、キャッシュが決して古くならないことを保証できます。 いつものように、変更をコミットしてデプロイします。

$ git commit -am "Keep Memcache up to date."
$ git push heroku master

新しいタスクを追加すると、キャッシングの実装以降に追加したすべてのタスクが表示されるようになりました。

Django の統合キャッシングの使用

Django には、Memcache を使用してパフォーマンスを改善する組み込みの方法もいくつか存在します。 これらの主なターゲットは、コストが高く CPU に負担がかかる操作である HTML のレンダリングです。

キャッシングと CSRF

トークンはリクエストごとに変わるため、CSRF トークンを使用するフォームが含まれるビューまたはフラグメントはキャッシュできません。Django の統合キャッシングの使用方法を学ぶために、Django の CSRF ミドルウェアを無効化してみましょう。 このタスクリストは公開されているので問題ありませんが、これは、重要な用途がある本番環境のアプリケーションでは行わないでください。

django_tasklist/settings.py​ で、CsrfViewMiddleware​ をコメントアウトします。

MIDDLEWARE = [
    # ...
    # 'django.middleware.csrf.CsrfViewMiddleware',
    # ...
]

テンプレートフラグメントをキャッシュする

Django では、レンダリングされたテンプレートフラグメントをキャッシュできます。これは、Flask でのスニペットキャッシング、あるいは Laravel でのレンダリングされたパーシャルのキャッシングに似ています。フラグメントキャッシングを有効にするには、テンプレートの先頭に {% load cache %}​ を追加します。

CSRF トークンを使用するフォームが含まれたフラグメントはキャッシュしないでください。

タスクエントリのレンダリングされたセットをキャッシュするために、mc_tasklist/templates/index.html​ で {% cache timeout key %}​ ステートメントを使用します。

{% load cache %}
<!-- ... -->

<table class="table table-striped">
  {% for task in tasks %}
    {% cache None 'task-fragment' task.id %}
    <tr>
      <!-- ... -->
    </tr>
    {% endcache %}
  {% endfor %}
</table>

<!-- ... -->

ここで、タイムアウトは None​ であり、キーは連結される文字列のリストです。タスク ID が再利用されない限り、これが、レンダリングされたスニペットのキャッシングのすべてです。Heroku で使用する PostgreSQL データベースでは ID を再利用しないため、条件は整っています。

ID を再利用する​データベースを使用する場合は、それぞれのタスクが削除された時点でフラグメントを削除する必要があります。次のコードをタスク削除ロジックに追加することで、これを実現できます。

from django.core.cache.utils import make_template_fragment_key
key = make_template_fragment_key("task-fragment", vary_on=[str(item.id)])
cache.delete(key)

アプリケーションでフラグメントをキャッシュした効果を見てみましょう。

$ git commit -am 'Cache task entry fragment'
$ git push heroku master

(最初のリロードを除き) ページをリロードするたびに、リスト内の各タスクで get hit​ の増加が確認できるはずです。

ビュー全体をキャッシュする

さらに一歩進んで、フラグメントの代わりにビュー全体をキャッシュすることができます。ビューが頻繁に変更される場合やユーザー入力用のフォームがビューに含まれている場合、意図しない副作用が発生することがあるため、これは慎重に行ってください。今回のタスクリストの例では、タスクが追加または削除されるたびにタスクリストが変更され、ビューにはタスクを追加および削除するためのフォームが含まれているため、この条件の両方が当てはまります。

CSRF トークンを使用するフォームが含まれたビューはキャッシュしないでください。

mc_tasklist/views.py​ で @cache_page(timeout)​ デコレーターを使用してタスクリストビューをキャッシュできます。

# ...
from django.views.decorators.cache import cache_page

@cache_page(None)
def index(request):
    # ...

# ...

タスクを追加または削除するたびにビューが変更されるため、これが起きるたびにキャッシュされたビューを削除する必要があります。これは簡単ではありません。後からビューを削除できるようにするには、ビューがキャッシュされたときにキーを把握しておく必要があります。

# ...
from django.utils.cache import learn_cache_key

VIEW_KEY = ""

@cache_page(None)
def index(request):
    # ...
    response = render_to_response('index.html', c)
    global VIEW_KEY
    VIEW_KEY = learn_cache_key(request, response)
    return response

def add(request):
    # ...
    cache.delete(VIEW_KEY)
    return redirect("/")

def remove(request):
    item = Task.objects.get(id=request.POST["id"])
    if item:
        # ...
        cache.delete(VIEW_KEY)
    return redirect("/")

ビューのキャッシングの効果を確認するには、アプリケーションをデプロイします。

$ git commit -am 'Cache task list view'
$ git push heroku master

最初の更新で、存在しているタスクの数に応じて get hit​ カウンターが増加するだけでなく、現在キャッシュされているビューに対応する形で get miss​ と set​ が増加するはずです。ビュー全体が 2 つの get​ コマンドで取得されるため、それ以降のリロードでは get hit​ カウンターは 2 しか増加しません。

ビューのキャッシングは、コストの高い操作またはテンプレートフラグメントのキャッシングを陳腐化しない​ことに注意してください。キャッシュされる大きな操作内で小さな操作をキャッシュしたり、大きなフラグメント内で小さなフラグメントをキャッシュしたりするのは良いやり方です。(ロシア人形方式キャッシングと呼ばれる) この手法では、構築単位をゼロから作り直す必要がないため、大きい方の操作、フラグメント、またはビューがキャッシュから削除された場合のパフォーマンスに寄与します。

セッションストレージでの Memcache の使用

Heroku では、再起動時に内容が失われる一時的なファイルシステムが dyno に備わっているため、セッション情報をディスクに保存することは推奨されていません。

Memcache は、タイムアウトがある短命セッションの情報を保存するのには適しています。しかし、Memcache はあくまでキャッシュであって永続的ではないため、寿命の長いセッションについては、データベースなどの永続的なストレージオプションのほうが適しています。

寿命の短いセッションの場合、django_tasklist/settings.py​ で、キャッシュバックエンドを使用するように SESSION_ENGINE​ を設定します。

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

寿命の長いセッションの場合、Django では、データベースに裏付けられたライトスルーキャッシュを使用できます。これは、永続性を保証しつつパフォーマンスを高めるための最良の選択肢です。ライトスルーキャッシュを使用するには、django_tasklist/settings.py​ で、次のように SESSION_ENGINE​ を設定します。

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

Django でセッションを使用する方法の詳細は、Django のセッションに関するドキュメント​を参照してください。

参考情報と資料

  • MemCachier アドオンページ
  • MemCachier ドキュメント
  • Memcache の高度な使用法
  • Heroku のための Django アプリの設定
  • Django チュートリアル
  • Django でのキャッシングに関するドキュメント

関連カテゴリー

  • Django の使用

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices