かきノート

【Laravel】(Alpine)Redis を使う時のライブラリ、Predis と PhpRedis を試してみる

July 16, 2021 • ☕️☕️☕️ 15 min read

【 環境 】
Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7
MySQL のバージョン: 5.7

Laravel で Redis を使う時のライブラリ

Laravel で Redis を使う時、PHP のライブラリに以下の2つの候補が出てくる。

デフォルトだと、composer.lock に「predis」が記述されている。

composer.lock

    "packages": [
        {
            "require-dev": {
                "predis/predis": "^1.1.2",
                "symfony/cache": "^5.1.4"
            },

しかし、config\database.php には「phpredis」が記述されている。

config\database.php

    'redis' => [
        'client' => env('REDIS_CLIENT', 'phpredis'),

何なんだこの中途半端な状態は。

と思って調べたら、predis はデフォルトから外される予定だったけど、やっぱ外すのやめたーって事になり、とはいっても phpredis を推奨したいし・・・
といった感じで、どっちつかずの状態になっているみたい。

どちらを採用するべき?

どちらを使えばいいのかについては議論が活発なようで、こんなのがありました。

PHP向けRedisクライアントのpredisを使うのは止めた方がいいです

predisはPHP向けのRedisクライアントです。

リポジトリを見て察しのいい方は気づいたと思うのですが、2年ぐらいメンテ(コミットすら)されていません。

すでにPHP7.3周りで問題が出てます。これが「どういった事情でメンテされていないのか?」「されないのではなく、できないのか?」などは誰もわからないと思いますし、もしかしたら将来的に再度メンテされる可能性はありえます。

(今はどうなのか気になって調べてみたところ、2021年7月現在は、無事開発は再開されているようです。)

Laravelのドキュメントにも書いてありますが、phpredis使えばいいと思います。
こちらはcomposerでインストールできない(PHP拡張モジュールなので)のでpeclでインストールします。

いやああああ!!!
マジなの? 令和にもなって、PHPライブラリを composer でインストールできないとか正気なの??

さすがに今では改善されているんじゃ・・・と思って公式サイトの PhpRedis 公式のインストールガイドを見てみた。
が、pecl を使えという夢も希望も無いメッセージが。
https://github.com/phpredis/phpredis/blob/develop/INSTALL.markdown

こんなのもあった。
How to install phpredis using composer?

I need to install phpredis using composer on windows server running IIS. What is the package name for this?

You cannot. Composer is for pure-PHP library not for C extension (pickle will do it later)

You need to compile it! See more information here:

どうやら composer でインストールするのは絶望的で、今後もできるようになる可能性は限りなくゼロに近そう。

念のため、他の記事も参考にしてみた。
Laravel 8のRedisクライアントのパフォーマンスの比較

性能比較をして、PhpRedis の方が上だった模様。

上記のコマンドをそれぞれ50回実行し、平均(±標準偏差)を算出しました。

  • PhpRedis … 1.317 ± 0.03 (秒)
  • Predis … 1.509 ± 0.026 (秒)

Predisを使用した場合の処理時間はPhpRedisの場合の約1割増しと、明らかに差がでました

Laravel 8 でPhpRedisとPredisを比較しました。PhpRedisの方が普通に速いので、導入できる環境であれば、Laravelの推奨に従ってPhpRedisで良さそうです。

でも composer で管理できないのは嫌だ。

PhpRedis vs Predis: Comparison on real production data

PhpRedis is faster about x6 times. Using igbinary serializer reduces stored data size about 3x times.

If Redis installed on separate machines, reducing network traffic is a very significant speedup.

ここでも PhpRedis を絶賛。

以下、Laravel公式の見解。
Redis 8.x Laravel

LaravelでRedisを使い始める前に、PECLにより phpredis HP拡張機能をインストールして使用することを推奨します。

これはもう phpredis で確定か。

Alpine について

そうなると、docker コンテナを弄らないといけない・・・
今使っているコンテナ、Alpine なんだよな。。

Alpine は、軽量な Linux ディストリビューション。
Docker コンテナのイメージのベースに使われる事も多くなってきた。

ちなみに当方、Alpine は、あまり積極的に採用したくない派です。

理由は以下。
Dockerイメージのベースディストリは、Alpine がいい感じなの?

ざっくり言うと、Alpine の嫌な所、こんな感じ。

  • apk というマイナーなパッケージマネージャーを採用している
  • パッケージのバージョンがそれほど新鮮でない
  • なので、最新パッケージはソース持ってきてコンパイルする必要がある
  • ash というマイナーなシェルが採用されている(シェルでハマるのが嫌)
  • Dockerfile作りが職人芸になりがち
  • Debian や RedHat と比べるとマイナーなので、ハマった時のググラビリティが低い
  • というか「apk」も「ash」もググラビリティが低い(同一単語で、もっとメジャーなものがある)
  • マイナーディストリは色々とトラウマ
  • 他のディストリと比較して、パフォーマンスは出ないことがあるらしい(個人では未計測です)
  • Docker公式の『Dockerfile 記述のベスト・プラクティス』にて、Debian イメージをお勧めしている
  • Alpine 使うのに、「軽量化」以外のメリットあるんか。デメリットの方が大きく上回っとりゃせんか?
  • ビルド時間を 5分縮めるために、エンジニアの時間を 100時間ぐらい溶かしてない? それって本当に費用対効果いいの? 軽量化にこだわるあまり、色々な事を犠牲してない?

何てこった! 自分がこんなに Alpine に悪い印象持ってたなんて!

ちなみに Alpine に否定的な意見を持ってるのは、自分だけじゃないぞ。(道連れ)
軽量Dockerイメージに安易にAlpineを使うのはやめたほうがいいという話

とはいえ、文句たれてても仕方ないんで、試しにインストールしてみよう。

PhpRedis を Alpine で使う

という事で、公式のインストールガイドに沿ってみる。
https://github.com/phpredis/phpredis/blob/develop/INSTALL.markdown

コンテナビルドの前に、まずはコンテナログインしてインストールできるかを確認してみる。

以下、インストールコマンド。

pecl install redis

実行結果。

/application # pecl install redis
downloading redis-5.3.4.tgz ...
Starting to download redis-5.3.4.tgz (268,154 bytes)
........................................................done: 268,154 bytes
29 source files, building
running: phpize
Configuring for:
PHP Api Version:         20190902
Zend Module Api No:      20190902
Zend Extension Api No:   320190902
Cannot find autoconf. Please check your autoconf installation and the
$PHP_AUTOCONF environment variable. Then, rerun this script.

ERROR: `phpize' failed

予想通り、上手く行かない。

これで行けるのか? https://github.com/docker-library/php/issues/412

コマンド

apk add --no-cache \
        $PHPIZE_DEPS \
        openssl-dev

実行結果

/application # apk add --no-cache \
>         $PHPIZE_DEPS \
>         openssl-dev
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/22) Upgrading musl (1.2.2-r0 -> 1.2.2-r1)

中略

(22/22) Installing re2c (1.3-r1)
Executing busybox-1.32.1-r0.trigger
Executing ca-certificates-20191127-r5.trigger
OK: 357 MiB in 81 packages

行けたっぽい。

という事で、気を取り直して、「pecl install redis」を再実行。

/application # pecl install redis
downloading redis-5.3.4.tgz ...
Starting to download redis-5.3.4.tgz (268,154 bytes)
........................................................done: 268,154 bytes
29 source files, building
running: phpize
Configuring for:
PHP Api Version:         20190902
Zend Module Api No:      20190902
Zend Extension Api No:   320190902
enable igbinary serializer support? [no] : 

(中略)

Build complete.
Don't forget to run 'make test'.

(中略)

Build process completed successfully
Installing '/usr/local/lib/php/extensions/no-debug-non-zts-20190902/redis.so'
install ok: channel://pecl.php.net/redis-5.3.4
configuration option "php_ini" is not set to php.ini location
You should add "extension=redis.so" to php.ini

どうやら上手く行ったみたい。

あとは、Laravel の設定を変更。

.env

CACHE_DRIVER=redis

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

config\database.php

    'redis' => [

        'client' => env('REDIS_CLIENT', 'phpredis'),

あとはキャッシュをクリア

php artisan config:clear
php artisan config:cache

無事、Redis を動かす事が出来ました。

ちなみに、web.php に以下のような超適当なコードを書いて実験してみました。

routes\web.php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Cache;

//============================================================================================
//                                       Cache
//============================================================================================
// http://localhost:8000/cache/put
Route::get('/cache/put', function () {
    echo "put";
    Cache::put('key01', 'value01');
});

Route::get('/cache/remember', function () {
    echo "remember";

    $cache = \Cache::remember('key02', 10, function(){
        return "value02_remember";
    });

    dump($cache);
});

Route::get('/cache/get', function () {
    echo "get";

    $cache01 = Cache::get('key01');
    $cache02 = Cache::get('key02');

    dump($cache01);
    dump($cache02);
});


Route::get('/cache/forget', function () {
    echo "forget";

    Cache::forget('key01');
    Cache::forget('key02');
});

その他注意事項

pecl install redis コマンドにてインストールした後、以下のメッセージが表示されています。

You should add “extension=redis.so” to

との事ですが、別に追記せずとも動きました。
詳細は私も良く分かってないのですが、Laravel が気を利かせて、勝手に読みに行ってるとか?

Laravel Redisのライブラリをインストールしたらエラーが発生した

こちらでは、
『エラーが出たけど、php.ini「extension=“redis.so」をコメントアウトしたら直った。』
といった内容が書かれています。

という事は、Laravel を使う場合は、この設定は不要なのかもしれません。

ただ、別の環境にアップする時にはエラーが発生するケースがあるようです。
AWSにLaravelをデプロイしたらエラーが出たときの対応方法

EC2 にアップすると動かなかったので、php.ini に extension=redis.so を追記しているようです。

「本番環境では動かない!」という場合のため、こんな現象があるという事を記憶に留めておいた方がよさそう。

おそらく Amazon Linux(RedHat系)を使っているのも、考えられる原因の1つでしょうか。
後で試してみたのですが、Debian では特に問題なく動作できました。

php.ini に extension=redis.so を追記

まずは php.ini のパスを確認。
以下、コマンド。

php -i | grep php.ini

実行結果例

/application $ php -i | grep php.ini
Configuration File (php.ini) Path => /usr/local/etc/php
Loaded Configuration File => /usr/local/etc/php/php.ini

上記では、保存パスが「/usr/local/etc/php/php.ini」だったので、追記パスは以下。

echo "extension=redis.so" >> /usr/local/etc/php/php.ini

追記後は php.ini の内容を確認。

その後、再起動。

Predis を Alpine で使う

こちらも試してみました。

composer でインストールができるので、PhpRedis よりも遥かに楽です。

詳細は公式サイトを。
https://readouble.com/laravel/8.x/ja/redis.html

後は、アプリ動かそうとしたら
「Predis Client not found」
というメッセージが出てきたので、ググって解決したという感じでしょうか。

.env や config を正しく設定できていれば、すんなり行くのではないかと思います。

採用選定基準についての所管

PhpRedis 速い! PhpRedis 最高! 絶対 PhpRedis にするべき!
みたいな意見もちらほら見かけるけど、結局、OS に手を加えざるを得ず、そこに余計に気を回さないといけなくなり、環境構築の難易度を上げるぐらいなら、いっそ Predis を選択するのもアリなのでは?
と思った。

開発が中断されるのが懸念事項に上がってけど、少なくとも今は再開しているし、composer で管理できるしで、色々メリットはある。

性能は PhpRedis の方が上だけど、システムによってはキャッシュへのアクセスがそこまで頻繁に起こらないケースもあるだろうし、そこまで厳しいアクセススピードの性能が求められないなら、管理コストを下げられるライブラリを選ぶ、というのも1つの選定基準だと思う。

少なくとも自分は、上記のような理由で PhpRedis ではなく Predis を採用する責任者が居たとしても何ら疑問はないし、その意見に反対するつもりも無い。

ちなみに自分は
「高速化と軽量化は常に正義!(たとえ厳しく求められないケースであっても。技術負債を抱えてでも実施するべき!)」
という意見には否定的です。

が、現在の場面においては、オフィシャルのアナウンス通り、PhpRedis にしておこうかと思います。

まとめ

  • Laravel で Redis を使う場合、2種類のライブラリのうち、どちらかを採用する。「Predis」か「PhpRedis」
  • 昔は Predis が使われていたが、現在では PhpRedis が推奨されている。(公式でさえ)
  • PhpRedis は composer で管理できず、pecl でインストールする必要がある
  • そのため、コンテナを使っている場合、イメージを作り替える必要がある
  • php.ini に “extension=redis.so” の追記が必要かもしれないけど、無くても動く。ただし環境によるかも。

キャッシュドライバを使うだけで、何でこんなに苦労を・・

終わりに

という事で、時代の流れに沿って PhpRedis を使う事にしたんだけど、コンテナイメージ作り直すの面倒くせええええ!!!

PHP ライブラリは composer で管理したい。


おまけ(PhpRedis を Debian で使う)

以下、インストールコマンド。

pecl install redis

特に何も問題が出ることはありませんでした。
やっぱ Debian の方がいいんじゃないのか・・・?


追記

ある日、何をやってもブラウザに「500 エラー」としか出なくなってたので、laravel.log を見たら、こんなの出てた。

local.ERROR: Please make sure the PHP Redis extension is installed and enabled. 
{"exception":"[object] (LogicException(code: 0): Please make sure the PHP Redis extension is installed and enabled. 
at /var/www/html/my-laravel-app/vendor/laravel/framework/src/Illuminate/Redis/Connectors/PhpRedisConnector.php:77)

あれ? 特に何も触ってないのに?
と思いきや、.env を弄ってて、環境設定ファイルが読み込みエラーになってたのが原因だった。

それ以外の全てのエラーを優先して前面に出て来るとは、なかなか主張が強いな・・


Relative Posts:

【Laravel】$schedule->command にて、cron メソッドの引数が不正だった場合、全てのコマンドが実行されない。

July 20, 2021

【Laravel】eloquent Model の「query()」と「newQuery()」の違いとは?

July 15, 2021

福岡の物流エンジニアが、七転び八起きしたあと九回転び、寝っ転がったまま何かやってる事を垂れ流しているブログ

RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon