概要
データベースへの読み込みが多いアプリケーションに Amazon ElastiCache を足すと、同じデータの読み込みをキャッシュで受け止めて、応答を速くし、データベースの負荷を下げられます。ただし、キャッシュは置いただけでは使われません。いつキャッシュに書き、いつキャッシュから読むかを、アプリケーションのコードで決める必要があります。
代表的な書き方(キャッシュ戦略)が、遅延読み込み(レイジーローディング) と 書き込みスルー です。遅延読み込みは「まずキャッシュを読み、無ければデータベースから読んでキャッシュに書く」、書き込みスルーは「データベースに書くたびにキャッシュも更新する」書き方です。
この記事では、2 つの戦略の処理の流れと、それぞれの長所・短所(古いデータ、使われないデータ)、TTL の役割を整理します。実践では、ElastiCache for Memcached と DynamoDB を使い、遅延読み込みでキャッシュミスとキャッシュヒットが起きる様子、データベースだけを更新したときに古い値が返る様子、書き込みスルーでそれが解消する様子を確かめます。

クラウドおさるよく聞かれる値段は、手元の帳面(キャッシュ)に書いておけば、いちいちバナナ倉庫(データベース)まで走らずに済むでござる。問題は、いつ帳面に書くかでござるよ。
この記事のメリット
- 遅延読み込みと書き込みスルーの処理の流れを、疑似コードで書けるようになる
- 2 つの戦略の長所と短所(古いデータ、使われないデータ、書き込みの遅さ)を比べられるようになる
- TTL で古いデータとキャッシュの肥大化を抑える考え方が分かる
- ElastiCache のメトリクスでキャッシュのヒットとミスを確認できるようになる
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
遅延読み込み(レイジーローディング)
遅延読み込みは、データが必要になったときに初めてキャッシュに載せる戦略です。
record_value = cache.get(record_key)
if record_value == NULL: # キャッシュミス
record_value = db.query("SELECT Details FROM Records WHERE ID = ?", record_key)
cache.set(record_key, record_value) # 次に備えてキャッシュに書く
return record_value- キャッシュにあれば(キャッシュヒット)、データベースを読まずに返す
- キャッシュに無ければ(キャッシュミス)、データベースから読み、キャッシュに書いてから返す
キャッシュを読まずにいきなりデータベースを読む書き方や、データベースへの書き込み(UPDATE)のたびにキャッシュを更新する書き方は、遅延読み込みではありません。
書き込みスルー
書き込みスルーは、データベースに書き込むたびにキャッシュも同じ値で更新する戦略です。
db.query("UPDATE Records SET Details = ? WHERE ID = ?", record_value, record_key)
cache.set(record_key, record_value)

長所と短所
| 項目 | 遅延読み込み | 書き込みスルー |
|---|---|---|
| キャッシュに載るデータ | 実際に読まれたデータだけ | 書き込まれたデータすべて(読まれないものも) |
| キャッシュノードの障害 | 致命的ではない(ミスが増えて遅くなるだけ) | 更新が載らない期間が生じる |
| キャッシュミスのとき | データベースへの往復が増え、最初の読み込みが遅い | 書き込み済みのデータならミスは少ない |
| 古いデータ | データベースだけが更新されると、キャッシュに古い値が残る | キャッシュも一緒に更新されるので古くなりにくい |
| 書き込みの遅さ | 影響なし | 書き込みのたびにキャッシュへの書き込みも増える |



遅延読み込みの帳面は、倉庫の値段が変わっても自分からは書き換わらぬのでござる。古い値段で売ってしまわぬよう、帳面の書き込みに期限(TTL)を付けておくのが肝でござるぞ。
TTL(有効期限)
キャッシュに書くときに TTL(有効期限)を付けると、期限が切れたキーは読み込もうとしても見つからないもの(キャッシュミス)として扱われ、次の読み込みでデータベースから新しい値を取り直します。
- 遅延読み込みでは、古いデータが残り続ける期間の上限になる
- 書き込みスルーでは、一度も読まれないデータがキャッシュを埋め続けるのを防ぐ
実際には、遅延読み込みと書き込みスルーを組み合わせ、TTL も付けて使うことが多くあります。
実践
EC2 インスタンス dva-cache-app から、ElastiCache for Memcached のクラスター dva-cache-lab と DynamoDB の dva-cache-records(データベース役)を使います。用意済みの Python スクリプトで、遅延読み込みと書き込みスルーの動きを確かめます。
前提条件
- リージョン:
ap-northeast-1(東京) - 以下のリソースが作成済みであること
まだ用意していない場合は、「作り方の参考」の記事の手順で作れます。リソース名は、この記事の名前に読み替えてください。
| リソース種別 | リソース名 | 内容 | 作り方の参考 |
|---|---|---|---|
| VPC・サブネット | dva-cache-vpc | パブリックサブネット 2 つ(1a / 1c) | ― |
| ElastiCache クラスター | dva-cache-lab | Memcached、cache.t4g.micro × 1 ノード。SG で dva-cache-app-sg からの 11211 番ポートを許可 | Amazon ElastiCache でキャッシュを置いて読み込みを速くする(※この記事は Memcached を使う) |
| DynamoDB テーブル | dva-cache-records | パーティションキー record_id。rec-001〜rec-003 の 3 件(details に品名と値段) | Amazon DynamoDB でテーブルを作り Lambda から読み書きする |
| EC2 インスタンス | dva-cache-app | Amazon Linux 2023。Session Manager で接続でき、/opt/cache-lab/ に下の 3 つのスクリプトと、キャッシュとテーブルへの接続を設定した common.py がある | Amazon EC2 でインスタンスを起動しSSH接続する |
| スクリプト | 処理 |
|---|---|
lazy_get.py <キー> | 遅延読み込み。キャッシュを読み、無ければ DynamoDB から読んで TTL 300 秒でキャッシュに書く。HIT / MISS と処理時間を表示する |
update_db.py <キー> <値> | DynamoDB だけを更新する(キャッシュは触らない) |
write_through.py <キー> <値> | 書き込みスルー。DynamoDB を更新し、キャッシュも同じ値で更新する |
作成するリソース一覧
この実践では新しいリソースは作りません(キャッシュとテーブルの中身を読み書きします)。
手順 1: キャッシュクラスターを確かめる
AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。上部の検索バーに ElastiCache と入力し、「ElastiCache」を選択します。左側ナビゲーションの「Memcached キャッシュ」で dva-cache-lab を開き、ステータスが「Available」、エンジンが Memcached であることと、設定エンドポイント(dva-cache-lab.xxxxxx.cfg.apne1.cache.amazonaws.com:11211 の形)を確認します。スクリプトは、このクラスターのノードのエンドポイント(.0001. が入った名前)に接続するよう common.py に設定してあります。


手順 2: 遅延読み込みでキャッシュミスとヒットを見る
EC2 のコンソールで dva-cache-app を選択し、「接続」→「Session Manager」タブ →「接続」をクリックします。開いたターミナルで、次のコマンドを実行します。
cd /opt/cache-lab
export PYTHONWARNINGS=ignore
python3 lazy_get.py rec-001
python3 lazy_get.py rec-001
python3 lazy_get.py rec-001export PYTHONWARNINGS=ignore は、Amazon Linux 2023 の Python 3.9 に対して boto3 が出すサポート終了の警告を表示しないためのものです(動作には影響しません)。
1 回目は MISS rec-001 -> banana: 100 yen (38.5 ms) ※データベースから読んでキャッシュに書いた、2 回目以降は HIT rec-001 -> banana: 100 yen (1.4 ms) のように表示されます(ミリ秒の値は実行のたびに変わります)。HIT のときはデータベースを読まないので、処理時間も短くなります。1 回目から HIT になった場合は、以前の実行の値がキャッシュに残っています。TTL の 300 秒が過ぎると MISS から始まります。


手順 3: データベースだけを更新して、古い値が返ることを見る
ほかのアプリケーションがデータベースの値段を変えた場面を再現します。
python3 update_db.py rec-001 "banana: 120 yen"
python3 lazy_get.py rec-001データベースは banana: 120 yen に変わったのに、lazy_get.py は HIT rec-001 -> banana: 100 yen と古い値を返します。遅延読み込みでは、キャッシュに値が残っている間(TTL の 300 秒が過ぎるまで)、データベースの変更が反映されません。





倉庫の値段は 120 になったのに、帳面には 100 のままでござる。帳面を見るだけで倉庫に行かぬのが遅延読み込みの速さの秘密でござるが、裏を返せば、こういうずれも起きるのでござるな。
手順 4: 書き込みスルーで更新する
書き込みスルーで、データベースとキャッシュを一緒に更新します。
python3 write_through.py rec-001 "banana: 130 yen"
python3 lazy_get.py rec-001今度は HIT rec-001 -> banana: 130 yen と、新しい値がキャッシュから返ります。書き込みのたびにキャッシュも更新したので、古い値が残りません。


手順 5: キャッシュのメトリクスを見る
ElastiCache のコンソールで dva-cache-lab を開き、「メトリクス」タブを選択します。メトリクスは数十個あるので、検索欄に Get と入力して絞り込みます。「Get ヒット」(GetHits、キャッシュヒットの数)と「Get ミス」(GetMisses、キャッシュミスの数)のグラフに、ここまでの操作が反映されています(グラフに点が出るまで数分かかります)。ヒットの割合が高いほど、キャッシュがデータベースの読み込みを肩代わりできています。





読むときに埋めるのが遅延読み込み、書くときに埋めるのが書き込みスルー。ずれを嫌うなら書き込みスルーを足し、TTL で古さに上限を付ける。試験では、疑似コードの順番で見分けるのでござる。
まとめ
| 戦略 | キャッシュに書くとき | 弱点 | 補い方 |
|---|---|---|---|
| 遅延読み込み | 読み込みでキャッシュミスしたとき | 古いデータ、最初の読み込みが遅い | TTL、書き込みスルーとの併用 |
| 書き込みスルー | データベースに書き込むたび | 読まれないデータでキャッシュが埋まる、書き込みが重くなる | TTL |
- 遅延読み込みは「キャッシュを読む → 無ければデータベースを読む → キャッシュに書く」の順
- 書き込みスルーは「データベースに書く → キャッシュも同じ値で更新する」
- 遅延読み込みでは、データベースだけが更新されると、TTL が切れるまで古い値が返る
- TTL は、古いデータが残る期間と、使われないデータがキャッシュを埋め続けることの両方を抑える
- ElastiCache for Memcached では、
GetHits・GetMissesのメトリクスでヒットとミスを確認できる
参照先
- Memcached のキャッシュ戦略 – Amazon ElastiCache
- Amazon ElastiCache とは – Amazon ElastiCache
- Memcached のメトリクス – Amazon ElastiCache













