はじめに
今月は「知見のない技術を学ぼう月間」ということで、前回に引き続き、Redisを学習していきます。前回の記事がこちらになります。
Redisとは?Dockerで起動して基本コマンドを試してみた【超入門】Redisとは何かを初心者向けに解説します。 DockerでRedisを起動し、redis-cliを使ってSET・GET・EXPIRE・TTLなどの基本コマンドや、String・List・Hash型の操作を実際に試してみました。
前回はRedisの超基礎的な内容を学習しました。今回はより実践的にRedisを使えるように、Laravelアプリと連携して使ってみました。
私が使用した環境については以下の通りです。
Composer version 2.10.3 2026-08-27 13:34:23
PHP version 8.3.35 (/usr/local/bin/php)
Laravel Framework 13.37.0
ディレクトリ構成
redis-lab/
├── docker-compose.yml
├── .env # Docker Compose用の環境変数
├── .gitignore
├── README.md
├── docker-config/
│ ├── nginx/
│ │ ├── Dockerfile
│ │ └── default.conf
│ ├── php/
│ │ ├── Dockerfile
│ │ └── php.ini
│ ├── mariadb/
│ │ └── data/ # MariaDBの永続化データ
│ └── redis/
│ └── data/ # Redisの永続化データ
└── src/ # Laravelアプリケーション
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── .env # Laravel用の環境変数
├── artisan
├── composer.json
└── composer.lock
上記の環境をdockerで構築していきます。学習用に作成したプロジェクトもGitHubで公開していますので、こちらを参考にしてください。
GitHub – yu-corder/redis-lab: A Docker-based Laravel development environment with Redis and MariaDB for learning and experimenting with caching.A Docker-based Laravel development environment with Redis and MariaDB for learning and experimenting with caching. – yu-…
環境構築
docker や Laravel のコマンド等については深く触れませんので、ご容赦ください。
docker-compose.yml になります。※学習のための、ローカル開発環境用です。
services:
web:
build: ./docker-config/php
container_name: redis-php
volumes:
- ./src:/var/www/
depends_on:
- mariadb
nginx:
ports:
- "80:80"
build: ./docker-config/nginx
container_name: redis-nginx
volumes:
- ./src:/var/www
- ./docker-config/nginx/default.conf:/etc/nginx/http.d/default.conf
depends_on:
- web
mariadb:
image: mariadb
container_name: redis-mariadb
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
TZ: ${TZ}
volumes:
- ./docker-config/mariadb/data:/var/lib/mysql
redis:
image: "redis:7.4"
volumes:
- "./docker-config/redis/data:/data"
docker-configディレクトリ以下にDockerfileを配置します。
redis-lab/
├── docker-compose.yml
├── .env # Docker Compose用の環境変数
├── .gitignore
├── README.md
├── docker-config/
│ ├── nginx/
│ │ ├── Dockerfile
│ │ └── default.conf
│ ├── php/
│ │ ├── Dockerfile
│ │ └── php.ini
│ ├── mariadb/
│ │ └── data/ # MariaDBの永続化データ
│ └── redis/
│ └── data/ # Redisの永続化データ
└── src/ # Laravelアプリケーション
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── .env # Laravel用の環境変数
├── artisan
├── composer.json
└── composer.lock
php/Dockerfile ですが、PhpRedis拡張のインストールをします。また、docker-php-ext-enable redis でPECLを経由してインストールしたPHP拡張モジュールを有効化します。
FROM php:8.3-fpm
WORKDIR /var/www
ADD . /var/www
RUN chown -R www-data:www-data /var/www
RUN cd /usr/bin && curl -fsSL https://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
RUN apt-get update \
&& apt-get install -y \
gcc \
make \
git \
unzip \
vim \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libmcrypt-dev \
libpq-dev \
curl \
gnupg \
openssl \
&& docker-php-ext-install pdo_mysql mysqli \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install PhpRedis extension
RUN pecl install redis \
&& docker-php-ext-enable redis
COPY php.ini /usr/local/etc/php/
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get update \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g n \
&& n stable \
&& npm update -g npm
nginx/Dockerfile です。
FROM alpine:3.22
RUN apk update && \
apk add --no-cache nginx
RUN mkdir -p /run/nginx
CMD nginx -g "daemon off;"
こちらの内容でビルドと起動をします。
test@test redis-lab % docker compose up -d
...省略
test@test redis-lab % docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
872647ae3b34 redis-lab_nginx "/bin/sh -c 'nginx -…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp redis-nginx
1e74b78c7ec1 redis-lab_web "docker-php-entrypoi…" 4 minutes ago Up 4 minutes 9000/tcp redis-php
a464a43d5349 redis:7.4 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 6379/tcp redis-lab_redis_1
237a4ec54b27 mariadb "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp redis-mariadb
コンテナの起動には成功しています。続いてLaravelプロジェクトを作成します。
test@test redis-lab % docker-compose exec web composer create-project laravel/laravel .
//コンテナに入ってます。
root@1e74b78c7ec1:/var/www# php artisan --version
Laravel Framework 13.37.0
webコンテナ(Laravelアプリが動いているコンテナ)からRedis や DB に接続できるように.envファイル(srcディレクトリ直下の.envファイル)に追加をします。※envファイルはシステムの機密情報が盛りだくさんなので、リポジトリやブログに載せないようにしましょう。以下はローカル開発環境の.envということで、ブログに載せています。
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PORT=6379
CACHE_STORE=redis
DB_CONNECTION=mysql
DB_HOST=mariadb
DB_PORT=3306
DB_DATABASE=development
DB_USERNAME=root
DB_PASSWORD=root
次にマイグレーションを実行します。
test@test redis-lab % docker-compose exec web php artisan migrate
これで、mariadb に初期テーブルが作成されました。
次にwebコンテナからRedisに書き込めるか確認します。
test@test redis-lab % docker-compose exec web php artisan tinker
Psy Shell v0.12.24 (PHP 8.3.35 — cli) by Justin Hileman
New PHP manual is available (latest: 3.1.4). Update with `doc --update-manual`
> use Illuminate\Support\Facades\Cache;
Cache::put('test_key', 'Hello Redis', 300);
Cache::get('test_key');
= "Hello Redis"
>
//別コンソール
test@test redis-lab % docker-compose exec redis redis-cli
127.0.0.1:6379> SCAN 0 MATCH '*test_key*' COUNT 100
1) "0"
2) (empty array)
127.0.0.1:6379> SCAN 0 MATCH '*test_key*' COUNT 100
1) "0"
2) (empty array)
127.0.0.1:6379> SCAN 0 MATCH '*test_key*' COUNT 100
1) "0"
2) (empty array)
webコンテナで登録したキーが見つかりませんでした。Laravelの標準設定では、Redisのキャッシュ用接続にDB番号 1が使われことがあるみたいです。
> config('database.redis.cache.database');
= "1"
そのため、 Redis側で以下を実行します。
SELECT 1
再度キーを探して見つけることができました。
127.0.0.1:6379[1]> SCAN 0 MATCH '*test_key*' COUNT 100
1) "0"
2) 1) "laravel-database-laravel-cache-test_key"
127.0.0.1:6379[1]> TTL laravel-database-laravel-cache-test_key
(integer) 206
127.0.0.1:6379[1]> GET laravel-database-laravel-cache-test_key
"s:11:\"Hello Redis\";"
127.0.0.1:6379[1]>
Redisをキャッシュとして利用してみる
実際にRedisをキャッシュとして利用してみます。商品テーブルを新たに追加します。商品テーブルのカラムは以下です。詳細はリポジトリのマイグレーションファイルを参照してください。
$table->id();
$table->string('name');
$table->integer('price');
$table->timestamps();
docker-compose exec web php artisan make:model Product -m
docker-compose exec web php artisan migrate
docker-compose exec web php artisan make:controller ProductController
コントローラ と ルーティングを修正します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\Product;
class ProductController extends Controller
{
public function show(int $id)
{
$key = "product:{$id}";
DB::enableQueryLog();
$product = Cache::remember($key, 60, function () use ($id) {
return Product::findOrFail($id)->toArray();
});
return response()->json([
'product' => $product,
'queries' => DB::getQueryLog(),
]);
}
}
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/products/{id}', [ProductController::class, 'show']);
サンプルデータを作成します。
test@test redis-lab % docker-compose exec web php artisan tinker
Psy Shell v0.12.24 (PHP 8.3.35 — cli) by Justin Hileman
New PHP manual is available (latest: 3.1.4). Update with `doc --update-manual`
> use App\Models\Product;
$product = new Product();
$product->name = 'MacBook Pro';
$product->price = 250000;
$product->save();
= true
> Product::all();
= Illuminate\Database\Eloquent\Collection {#7510
all: [
App\Models\Product {#7509
id: 1,
name: "MacBook Pro",
price: 250000,
created_at: "2026-10-17 08:01:19",
updated_at: "2026-10-17 08:01:19",
},
],
}
> quit
ブラウザから確認してみます。キャッシュがない場合のレスポンスは以下のようになります。
{"product":{"id":1,"name":"MacBook Pro","price":250000,"created_at":"2026-10-17T08:01:19.000000Z","updated_at":"2026-10-17T08:01:19.000000Z"},"queries":[{"query":"select * from `products` where `products`.`id` = ? limit 1","bindings":[1],"time":2.16,"readWriteType":"write"}]}
コントローラで、クエリログを返すようにしてます。
return response()->json([
'product' => $product,
'queries' => DB::getQueryLog(),
]);
次にキャッシュが残っている場合のレスポンスです。
{"product":{"id":1,"name":"MacBook Pro","price":250000,"created_at":"2026-10-17T08:01:19.000000Z","updated_at":"2026-10-17T08:01:19.000000Z"},"queries":[]}
Cache::remember() がRedisに保存された商品データを返し、MariaDBへの問い合わせを省略できています。
| 項目 | 1回目(キャッシュなし) | 2回目(キャッシュあり) |
|---|---|---|
| Redisのキャッシュ | なし | あり |
| MariaDBへのSELECT | 実行 | 実行されない |
| 商品データ | MariaDBから取得 | Redisから取得 |
| キャッシュへの保存 | あり | なし |
ここまでで、Cache Asideパターンの基本的な動作を確認できました。
もう少し踏み込んで検証してみます。次にDBのデータを変更した時のキャッシュがどうなるか確認してみます。キャッシュの有効期限を一時的に300秒にします。
$product = Cache::remember($key, 300, function () use ($id) {
return Product::findOrFail($id)->toArray();
});
Cache::remember() の有効期限を60秒から300秒に変更しても、すでにRedisに存在するキャッシュのTTLが300秒に延長されるわけではありません。
新しいTTLが適用されるのは、キャッシュが存在せず、新たに保存されたときです。
ブラウザから接続して、キャッシュを作り直します。
{"product":{"id":1,"name":"MacBook Pro","price":250000,"created_at":"2026-10-17T08:01:19.000000Z","updated_at":"2026-10-17T08:01:19.000000Z"},"queries":[{"query":"select * from `products` where `products`.`id` = ? limit 1","bindings":[1],"time":2.16,"readWriteType":"write"}]}
商品テーブルの価格を変更します。
test@test redis-lab % docker-compose exec web php artisan tinker
Psy Shell v0.12.24 (PHP 8.3.35 — cli) by Justin Hileman
New PHP manual is available (latest: 3.1.4). Update with `doc --update-manual`
> use App\Models\Product;
Product::where('id', 1)->update(['price' => 200000]);
= 1
> Product::find(1)->price;
= 200000
>
再度ブラウザからアクセスします。
{"product":{"id":1,"name":"MacBook Pro","price":250000,"created_at":"2026-10-17T08:01:19.000000Z","updated_at":"2026-10-17T08:01:19.000000Z"},"queries":[]}
Redisにキャッシュされた古い価格 250000 が返ってきました。キャッシュを削除して、再度ブラウザからアクセスします。
Cache::forget('product:1');
レスポンス:
{"product":{"id":1,"name":"MacBook Pro","price":200000,"created_at":"2026-10-17T08:01:19.000000Z","updated_at":"2026-10-17T09:16:50.000000Z"},"queries":[{"query":"select * from `products` where `products`.`id` = ? limit 1","bindings":[1],"time":3.18,"readWriteType":"write"}]}
今回は手動で、Cache::forget() を実行しましたが、実際のアプリでは、商品データの更新処理が成功したタイミングで、対応するキャッシュを削除する設計が一般的です。
Cache-Aside パターン – Azure Architecture Centerオンデマンドでデータをキャッシュに読み込む方法について説明します。これにより、パフォーマンスが向上し、キャッシュされたデータと基になるデータ ストア間で整合性を維持できます。
最後に
今回はLaravelとRedisを連携させ、MariaDBの商品データをキャッシュする処理を実装してみました。
実際にSQLログを確認することで、キャッシュが存在する場合はDBへの問い合わせが省略されることを確認できました。一方で、DBのデータを更新してもキャッシュには古いデータが残るため、データの整合性を考慮する必要があることも学びました。
Redisをキャッシュとして利用する際は、単にデータを保存するだけでなく、キャッシュの有効期限や削除するタイミングも重要だと感じました。
最後まで読んでいただき、ありがとうございました。


