Djangoのキャッシュ機能を使って画面表示をもっと高速化する Memcached編 

#はじめに

Djangoのキャッシュ機能を使って、画面表示の高速化にトライしてみました。

今回、キャッシュを保存する先はMemcahcedとしています。

その他の保存先としてファイルや、データベースを使うこともできます。

データベースをキャッシュとする方法や、キャッシュ設定前後の処理時間の比較は、前回の記事「[Djangoのキャッシュ機能を使って画面表示を高速化する データベース編](https://qiita.com/shonansurvivors/items/5669a3c06030e7d4ce75)」を見てください。

#この記事の構成

1. Djangoの開発サーバーでMemcachedを使う方法について解説します
2. MemcachedによりDjangoの動きがどのように変わったかを見てみます

ここで説明するのはあくまで開発サーバーでMemcachedを使う方法ですので、本番環境で使う場合にはsettings.pyの設定を適宜変更する必要があります。

本番環境の一例としてHeroku環境のDjangoでMemcachedを使う方法については以下を参照ください。

- [Djangoのキャッシュ機能をHerokuで使う Memcached Cloud編](https://qiita.com/shonansurvivors/items/85baf2cd57c79c781dc2)

#環境

- macOS High Sierra 10.13.6
- Python 3.7.0
- Django 2.1
- python-memcached 1.59

#1. Djangoの開発サーバーでMemcachedを使う方法

##1.1. Memcachedをインストールする

```
$brew install memcached
```

##1.2. Memcachedを起動する
```
$ brew services start memcached
```


##1.3. python-memcachedをインストールする

pythonでmemcashedを使う為に、python-memcachedをインストールします。

```
(myvenv) $ pip install python-memcached
```

##1.4. settings.pyにキャッシュの設定を行う

`settings.py`に以下を追加します。

```python:settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}
```

`BACKEND`の部分がキャッシュの保存先の種類を示していて、Memcachedを使う場合は`memcached.MemcachedCache`と指定します。

`LOCATION`の部分は、Memcahcedが稼動するサーバーとポート番号を指定します。
Djangoの開発サーバーのIPアドレスが`127.0.0.1`なのであれば、`127.0.0.1:11211`と指定します。
`11211`は、Memcachedのデフォルトのポート番号です。

##1.5. キャッシュする対象を指定する

今回はアクセスの多い特定のURLのページだけをキャッシュすることにしました。そのような場合は、`urls.py`でキャッシュする対象を指定します。

もともとの`urls.py`が以下のような内容だった場合・・・

```python:urls.py（修正前）
from django.urls import path
from . import views

urlpatterns = [
    path('', views.ArticleListView.as_view(), name='article_list'),
    # 略
]
```

以下のように`cache_page`関数を使います。秒数の部分は`60 * 15`と記述することで15分であることをわかりやすくしています。

```python:urls.py（修正後）
from django.urls import path
from django.views.decorators.cache import cache_page # ここを追加
from . import views


urlpatterns = [
    # cache_page(秒数)(ビュー)を追加する
    path('', cache_page(60 * 15)(views.ArticleListView.as_view()), name='article_list'),
    # 略
]
```

#2. キャッシュによりDjangoの動きがどのように変わったかを見てみる

キャッシュ設定前後で何が変わったのかをdjango-debug-toolbarの機能を使って見てみることにします。

django-debug-toolbarの設定方法は以下を参考にさせていただきました。

[Djangoメモ(14) : Django Debug Toolbarでデバッグ情報を表示](https://wonderwall.hatenablog.com/entry/2018/03/13/233000)

##2.1. キャッシュ設定無し（Memcachedを停止）

```
$ brew services stop memcached
```

<img width="342" alt="スクリーンショット 2018-12-14 13.47.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/317369/7a2a4619-4ccd-3711-e88a-eb1c0f4f4120.png">

画面は自作のDjangoアプリです。
右端の黒いツールバー部分がdjango-debug-toolbarによって表示されている情報です。
「時刻」に表示されているのがCPUの処理時間だと思われます。約163ミリ秒かかっています。
なお、SQLは3本発行されてます。

ちなみに処理時間の内訳は以下のようになっています。

<img width="599" alt="スクリーンショット 2018-12-14 13.52.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/317369/8f2192c4-f154-c826-a4de-07e8e8889863.png">

##2.2. キャッシュ設定あり（memcached起動中）の時の初回のアクセス

[前回の記事](https://qiita.com/shonansurvivors/items/5669a3c06030e7d4ce75)ではキャッシュの保存先をデータベースとしましたが、その場合、初回アクセス時に関してはキャッシュテーブルへの書き込みが発生する都合上、逆に処理時間が増えてしまうという弊害がありました。

> 
処理時間が約335ミリ秒(+154ミリ秒)に増えてしまいました。
また、SQLの発行本数も14本(+9本)に増えています。
>
SQLの本数が増えたのはキャッシュ用のテーブルへの読み書きを行ったためのようです。
キャッシュの有無を調べるSQL、(キャッシュが無いので)ページの情報をキャッシュへ書き込むSQLが発行されていました。

Memcachedの場合はどうなるのでしょうか？

Memcachedを起動した上で、画面にアクセスします。

```
$ brew services start memcached
```

<img width="340" alt="スクリーンショット 2018-12-14 14.00.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/317369/c9bcfc7f-e4fa-935a-1830-c4dfcb42ee1a.png">

Memcached停止中での画面アクセスと比較して、処理時間は約164ミリ秒(+1ミリ秒)とほぼ変化はありません。
また、SQLの発行本数についても3本のままで、増加はありません。

##2.3. キャッシュ設定ありの時の2回目のアクセス

<img width="340" alt="スクリーンショット 2018-12-14 14.15.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/317369/11121299-08df-ff35-ab67-bf5bf8c33f95.png">

処理時間が約９ミリ秒と大幅に減りました(-154ミリ秒)。
また、SQLの発行本数は0本(-3本)となりました。

# まとめ

##Memcachedの場合

|条件                         |CPU処理時間(ms)|SQL本数|
|:---------------------------|-------------:|:----:|
| キャッシュ設定無し              |163           |3|
| キャッシュ設定あり・初回アクセス    |164           |3|
| キャッシュ設定あり・2回目のアクセス | 10           |0|

たったの1回ずつしかアクセスしていないので厳密な検証ではありませんが、初回のアクセスにおいてもキャッシュ設定無しの時より画面表示に時間がかかってしまうようなことは無く、2回目以降のアクセスは大幅に時間が短くなることが期待できるようです。

##ちなみにデータベースの場合

同一のDjangoアプリケーションで、キャッシュの保存先をデータベースとした場合の処理時間も記載しておきます。

この結果を見ると、キャッシュの保存先はデータベースよりもMemcachedとした方が良さそうですね。

|条件                         |CPU処理時間(ms)|SQL本数|
|:---------------------------|-------------:|:----:|
| キャッシュ設定無し              |163           |3|
| キャッシュ設定あり・初回アクセス    |326           |12|
| キャッシュ設定あり・2回目のアクセス | 22           |2|




#参考

- [現場で使える Django の教科書《実践編》](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Django-%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8%E3%80%8A%E5%AE%9F%E8%B7%B5%E7%B7%A8%E3%80%8B-%E6%A8%AA%E7%80%AC-%E6%98%8E%E4%BB%81-ebook/dp/B07L3DRGBT)
- [Django’s cache framework（英語）](https://docs.djangoproject.com/en/2.1/topics/cache/)
- [macでmemcachedを導入する手順]
(https://qiita.com/makotok7/items/9998b15f79fc7a53af24)