アプリケーションのパフォーマンスは、製品の成功に不可欠です。ユーザーが1秒未満のWebサイト応答時間を期待している環境では、アプリケーションの速度が遅い場合の結果は、ドルとセントで測定できます。何も販売していない場合でも、ページの読み込みが速いと、サイトへのアクセスエクスペリエンスが向上します。
サーバーがリクエストを受信してからレスポンスを返すまでの間にサーバーで発生するすべてのことにより、ページの読み込みにかかる時間が長くなります。一般的な経験則として、サーバー上で排除できる処理が多いほど、アプリケーションの実行速度は速くなります。処理後にデータをキャッシュし、次に要求されたときにキャッシュからデータを提供することは、サーバーへのストレスを軽減する1つの方法です。 このチュートリアルでは、アプリケーションを阻害するいくつかの要因を調査し、それらの影響を打ち消すためにRedisでキャッシュを実装する方法を示します。
無料ボーナス: ここをクリックして、無料のDjangoラーニングリソースガイド(PDF)にアクセスします。このガイドには、Python +DjangoWebアプリケーションを構築する際に避けるべきヒントとコツおよび一般的な落とし穴が示されています。
Redisとは何ですか?
Redisは、キャッシュエンジンとして使用できるメモリ内のデータ構造ストアです。データをRAMに保持するため、Redisはデータを非常に迅速に配信できます。キャッシングに使用できる製品はRedisだけではありません。 Memcachedはもう1つの人気のあるメモリ内キャッシュシステムですが、多くの人は、ほとんどの状況でRedisがMemcachedよりも優れていることに同意しています。個人的には、Redisキューなどの他の目的でRedisをセットアップして使用するのがいかに簡単であるかが気に入っています。
はじめに
キャッシングの概念を紹介するサンプルアプリケーションを作成しました。私たちのアプリケーションは以下を使用します:
- Django(v1.9.8)
- Djangoデバッグツールバー(v1.4)
- django-redis(v4.4.3)
- Redis(v3.2.0)
アプリをインストールする
リポジトリのクローンを作成する前に、virtualenvwrapperをまだインストールしていない場合は、インストールしてください。これは、プロジェクトに必要な特定のPython依存関係をインストールして、アプリに必要なバージョンとライブラリを個別にターゲットにできるツールです。
次に、ディレクトリをプロジェクトを保持する場所に変更し、サンプルアプリリポジトリのクローンを作成します。完了したら、ディレクトリを複製されたリポジトリに変更し、mkvirtualenv
を使用してサンプルアプリの新しい仮想環境を作成します コマンド:
$ mkvirtualenv django-redis
(django-redis)$
注:
mkvirtualenv
を使用して仮想環境を作成する また、それをアクティブにします。
pip
を使用して必要なすべてのPython依存関係をインストールします 、次に次のタグをチェックアウトします:
(django-redis)$ git checkout tags/1
データベースを構築し、サンプルデータを入力して、サンプルアプリの設定を完了します。管理者サイトにログインできるように、必ずスーパーユーザーも作成してください。以下のコード例に従って、アプリを実行して、正しく機能していることを確認してください。ブラウザの管理ページにアクセスして、データが正しく読み込まれていることを確認してください。
(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver
Djangoアプリを実行したら、Redisのインストールに移動します。
Redisのインストール
ドキュメントに記載されている手順を使用して、Redisをダウンロードしてインストールします。または、 apt-getなどのパッケージマネージャーを使用してRedisをインストールすることもできます。 または自作 OSによって異なります。
新しいターミナルウィンドウからRedisサーバーを実行します。
$ redis-server
次に、別のターミナルウィンドウでRedisコマンドラインインターフェイス(CLI)を起動し、Redisサーバーに接続することをテストします。キャッシュに追加するキーを検査するためにRedisCLIを使用します。
$ redis-cli ping
PONG
Redisは、開発者がデータストアを操作するために使用できるさまざまなコマンドを備えたAPIを提供します。 Djangoはdjango-redisを使用します Redisでコマンドを実行します。
テキストエディタでサンプルアプリを見ると、 settings.pyにRedisの設定が表示されています。 ファイル。 CACHES
を使用してデフォルトのキャッシュを定義します 組み込みのdjango-redisを使用した設定 バックエンドとしてキャッシュします。 Redisはデフォルトでポート6379で実行され、設定でその場所をポイントします。最後に言及するのは、 django-redis 類似のキーを区別しやすくするために、キー名にプレフィックスとバージョンを追加します。この場合、プレフィックスを「例」として定義しました。
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient"
},
"KEY_PREFIX": "example"
}
}
注 :キャッシュバックエンドを構成しましたが、どのビュー関数もキャッシュを実装していません。
アプリのパフォーマンス
このチュートリアルの冒頭で述べたように、サーバーがリクエストを処理するために行うことはすべて、アプリケーションの読み込み時間を遅くします。ビジネスロジックの実行とテンプレートのレンダリングの処理オーバーヘッドは、かなりの量になる可能性があります。ネットワーク遅延は、データベースのクエリにかかる時間に影響します。これらの要素は、クライアントがサーバーにHTTPリクエストを送信するたびに機能します。ユーザーが1秒間に多くのリクエストを開始している場合、サーバーがそれらすべてを処理するように機能するため、パフォーマンスへの影響が顕著になります。
キャッシュを実装するときは、サーバーにリクエストを1回処理させてから、それをキャッシュに保存します。同じURLのリクエストがアプリケーションによって受信されると、サーバーは結果を毎回新たに処理するのではなく、キャッシュから取得します。通常、キャッシュされた結果を存続させる時間を設定して、データを定期的に更新できるようにします。これは、古いデータの提供を回避するために実装する重要な手順です。
次の場合に当てはまる場合は、リクエストの結果をキャッシュすることを検討する必要があります。
- ページのレンダリングには、多くのデータベースクエリやビジネスロジックが含まれます。
- ユーザーがこのページに頻繁にアクセスします
- データはすべてのユーザーで同じです
- データは頻繁に変更されません。
パフォーマンスの測定から始める
リクエストを受信した後、アプリケーションが応答を返す速度をベンチマークすることにより、アプリケーションの各ページの速度をテストすることから始めます。
これを実現するために、loadtest、HTTPロードジェネレーターを使用してリクエストのバーストで各ページをブラストし、リクエストレートに細心の注意を払います。上記のリンクにアクセスしてインストールしてください。インストールしたら、/cookbook/
に対して結果をテストします URLパス:
$ loadtest -n 100 -k http://localhost:8000/cookbook/
1秒あたり約16件のリクエストを処理していることに注意してください:
Requests per second: 16
コードが何をしているのかを見ると、パフォーマンスを向上させるために変更を加える方法を決定できます。アプリケーションは、/cookbook/
へのリクエストごとに、データベースに対して3つのネットワーク呼び出しを行います。 、および各呼び出しが接続を開いてクエリを実行するのに時間がかかります。 /cookbook/
にアクセスします ブラウザでURLを入力し、[Djangoデバッグツールバー]タブを展開して、この動作を確認します。 「SQL」というラベルの付いたメニューを見つけて、クエリの数を読みます:
cockbook / services.py
from cookbook.models import Recipe
def get_recipes():
# Queries 3 tables: cookbook_recipe, cookbook_ingredient,
# and cookbook_food.
return list(Recipe.objects.prefetch_related('ingredient_set__food'))
cockbook / views.py
from django.shortcuts import render
from cookbook.services import get_recipes
def recipes_view(request):
return render(request, 'cookbook/recipes.html', {
'recipes': get_recipes()
})
アプリケーションは、潜在的に高価なロジックを使用してテンプレートもレンダリングします。
<html>
<head>
<title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
<h1>{{ recipe.name }}</h1>
<p>{{ recipe.desc }}</p>
<h2>Ingredients</h2>
<ul>
{% for ingredient in recipe.ingredient_set.all %}
<li>{{ ingredient.desc }}</li>
{% endfor %}
</ul>
<h2>Instructions</h2>
<p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>
キャッシングの実装
ユーザーがサイトにアクセスし始めたときにアプリケーションが行うネットワーク呼び出しの総数を想像してみてください。 1,000人のユーザーがクックブックレシピを取得するAPIにアクセスすると、アプリケーションはデータベースに3,000回クエリを実行し、リクエストごとに新しいテンプレートがレンダリングされます。その数は、アプリケーションが拡大するにつれて増加するだけです。幸いなことに、このビューはキャッシングの優れた候補です。クックブックのレシピが変更されることはほとんどありません。また、クックブックの表示はアプリの中心的なテーマであるため、レシピを取得するAPIは頻繁に呼び出されることが保証されています。
以下の例では、キャッシュを使用するようにビュー関数を変更します。関数が実行されると、ビューキーがキャッシュにあるかどうかがチェックされます。キーが存在する場合、アプリはキャッシュからデータを取得して返します。そうでない場合、Djangoはデータベースにクエリを実行し、結果をビューキーを使用してキャッシュに格納します。この関数を初めて実行すると、Djangoはデータベースにクエリを実行してテンプレートをレンダリングし、Redisをネットワークで呼び出してデータをキャッシュに保存します。その後、関数を呼び出すたびに、データベースとビジネスロジックが完全にバイパスされ、Redisキャッシュにクエリが実行されます。
example / settings.py
# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15
cockbook / views.py
from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes
CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)
@cache_page(CACHE_TTL)
def recipes_view(request):
return render(request, 'cookbook/recipes.html', {
'recipes': get_recipes()
})
@cache_page()
を追加したことに注意してください ビュー機能のデコレータと、生きる時間。 /cookbook/
にアクセスします もう一度URLを入力し、Djangoデバッグツールバーを調べます。キーをチェックして保存するために、3つのデータベースクエリが実行され、キャッシュに対して3つの呼び出しが行われることがわかります。 Djangoは2つのキーを保存します(ヘッダー用に1つのキー、レンダリングされたページコンテンツ用に1つのキー)。ページをリロードして、ページアクティビティがどのように変化するかを観察します。 2回目は、データベースに対して0回の呼び出しが行われ、キャッシュに対して2回の呼び出しが行われます。現在、ページはキャッシュから提供されています!
パフォーマンステストを再実行すると、アプリケーションの読み込みが速くなっていることがわかります。
$ loadtest -n 100 -k http://localhost:8000/cookbook/
キャッシングにより総負荷が改善され、現在、1秒あたり21のリクエストを解決しています。これは、ベースラインより5つ多くなっています。
Requests per second: 21
CLIを使用したRedisの検査
この時点で、Redis CLIを使用して、Redisサーバーに何が保存されているかを確認できます。 Redisコマンドラインで、keys *
を入力します コマンド。任意のパターンに一致するすべてのキーを返します。 「example:1:views.decorators.cache.cache_page」というキーが表示されます。 「example」はキープレフィックス、「1」はバージョン、「views.decorators.cache.cache_page」はDjangoがキーに付ける名前であることを忘れないでください。キー名をコピーして、get
で入力します 指図。レンダリングされたHTML文字列が表示されます。
$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"
注:
flushall
を実行します データストアからすべてのキーをクリアするには、RedisCLIでコマンドを実行します。その後、キャッシュの有効期限が切れるのを待たずに、このチュートリアルの手順をもう一度実行できます。
まとめ
HTTPリクエストの処理にはコストがかかり、アプリケーションの人気が高まるにつれて、そのコストは増加します。場合によっては、キャッシュを実装することで、サーバーが実行する処理の量を大幅に減らすことができます。このチュートリアルでは、Redisを使用したDjangoでのキャッシュの基本に触れましたが、複雑なトピックの表面をざっと見ただけでした。
堅牢なアプリケーションにキャッシュを実装することには、多くの落とし穴と落とし穴があります。何をキャッシュするか、どのくらいの期間キャッシュするかを制御するのは困難です。キャッシュの無効化は、コンピュータサイエンスで難しいことの1つです。プライベートデータに意図したユーザーだけがアクセスできるようにすることはセキュリティの問題であり、キャッシュするときは非常に注意深く処理する必要があります。
無料ボーナス: ここをクリックして、無料のDjangoラーニングリソースガイド(PDF)にアクセスします。このガイドには、Python +DjangoWebアプリケーションを構築する際に避けるべきヒントとコツおよび一般的な落とし穴が示されています。
サンプルアプリケーションのソースコードを試してみてください。Djangoで開発を続けるときは、常にパフォーマンスを念頭に置いてください。