phpredis を使用した Redis Cluster の操作と設定

概要

Redis 3.0 以降ではクラスタリング機能が標準でサポートされており、PHP アプリケーションからこれを操作するには phpredis 拡張の RedisCluster クラス を利用する。このクラスは単一ノード用の Redis クラスと類似したインターフェースを提供するため、既存コードとの互換性が高く、ほとんどのメソッド呼び出しを変更せずにクラスタ環境に移行できる。 公式ドキュメント: https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#readme

クラスタへの接続方法

RedisCluster インスタンスを作成する際には、「シードノード」を指定することでクラスタ構成を自動検出する。以下の2つの方式が利用可能である。

1. シードノードを直接指定

複数のノードの中から1つ以上を初期接続先として渡す。内部的に CLUSTER SLOTS コマンドを実行し、キー空間全体のマッピングを行う。

// 複数のシードノードを指定
$cluster = new RedisCluster(null, ['192.168.1.10:7000', '192.168.1.11:7001']);

// タイムアウトと読み取りタイムアウトを設定(秒単位)
$cluster = new RedisCluster(null, ['192.168.1.10:7000'], 2.0, 1.5);

// 永続接続を有効化
$cluster = new RedisCluster(null, ['192.168.1.10:7000'], 2.0, 1.5, true);

2. redis.ini 経由での名前付きクラスタ定義

設定ファイル内で事前にクラスタ構成を登録しておくことで、名前だけでインスタンスを生成できる。

; redis.ini
redis.clusters.seeds = "prod[]=10.0.0.1:7000&prod[]=10.0.0.2:7000&dev[]=127.0.0.1:7000"
redis.clusters.timeout = "prod=3"
redis.clusters.read_timeout = "prod=5"

PHP 側では以下のように接続:

// 名前付きクラスタを初期化
$cluster = new RedisCluster('prod');

通信時のタイムアウト処理

Redis クラスタは高可用性を前提としているため、特定ノードへの接続失敗やタイムアウトが発生しても、フェイルオーバーによって代替ノードが応答できる。 RedisCluster は各コマンド送信時に開始時間を記録し、リダイレクト(MOVED/ASK)や再試行を繰り返すたびに経過時間をチェック。ユーザーが指定したタイムアウト値を超えると例外(RedisClusterException)を発生させる。

キー空間の動的更新

初期接続時に CLUSTER SLOTS により全ノードのハッシュスロット対応表を取得するが、クラスタ運用中にスロットが再分配される可能性がある。その場合、MOVED エラーを受け取った時点で内部マッピングを更新し、正しいノードにリクエストを転送する。 ASK リダイレクトに対しては、まず ASKING コマンドを送信したうえでキーを取得する、Redis 仕様に準拠した動作を行う。

スレーブノードの利用オプション

読み取り専用操作について、スレーブノードを利用するかどうかを設定できる。以下のオプションが利用可能:
// デフォルト:すべての読み書きをマスタへ
$cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE);

// マスタ障害時のみスレーブを利用(読み取り)
$cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_ERROR);

// 読み取りコマンドをマスタ・スレーブ間でランダム分散(ロードバランシング)
$cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE);

コマンド実行フロー

すべてのコマンドは内部的な「コマンドループ」を経由して処理される。以下の手順で実行される:
  1. 対象ノードにコマンドを送信
  2. MOVED/ASK 応答があればマッピング更新または再送
  3. 有効な応答を得るか、次のいずれかの条件に該当するまで繰り返す:
    • すべての既知ノードに接続不可
    • 指定タイムアウト時間超過
    • CLUSTERDOWN 状態が報告された
  4. 最終的に結果または例外を返却

トランザクションの扱い

MULTI/EXEC によるトランザクションはサポートされているが、複数ノードにまたがる場合がある点に注意が必要。トランザクションは最初のキー操作時に初めて対象ノード上で MULTI が発行される。
$cluster->multi();
$cluster->get('user:1000');
$cluster->set('user:1001', 'alice');

// user:2000 が別のノードにある場合、そちらでも MULTI 開始
$cluster->get('user:2000');

// exec の戻り値は常に配列。一部ノードで失敗しても全体としては成功とみなされ、
// 失敗した部分は false になる。
var_dump($cluster->exec());

パイプラインの非サポート

現時点の phpredis では、RedisCluster に対するパイプライン(pipeline)は。これは、キーのスロット配置が実行中に変化する可能性があり、パイプライン内の複数コマンドを安全に処理できないためである。必要に応じてアプリケーションレベルで代替実装を検討すること。

複数キー操作の制限

MGETMSET 以外のマルチキー命令(例: SINTERSTORE)は、すべてのキーがCROSSSLOT エラーが発生し、コマンドは失敗する。

MGET/MSET の特別扱い

これらのコマンドについては、内部的にキーごとにスロットを判定し、複数の個別コマンドに分割して実行する。たとえば以下のような呼び出し:
$result = $cluster->mget([
    '{session}abc123',
    '{session}def456',
    'standalone:key'
]);
→ 実際には「{session}」キー群と「それ以外」のキーで分けて2回のリクエストが発行される。

特定ノード向けコマンド

一部の管理系コマンド(例: INFO, CONFIG, CLUSTER)は特定のノードに対して実行する必要がある。この場合、引数にキーまたはホスト:ポートの配列を渡すことで対象を明示できる。
// キーのハッシュ先ノードに対して PING
$cluster->ping('user:1234');

// 特定ノード(ホスト+ポート)に対して INFO 取得
$cluster->info(['192.168.1.10', 7000]);

// 全マスタノードに対して CONFIG GET maxmemory を実行
foreach ($cluster->_masters() as $master) {
    $config = $cluster->config('GET', 'maxmemory', $master);
    echo "Node " . implode(':', $master) . ": $config\n";
}

対象ノード指定が可能な主なコマンド:

  • INFO, CONFIG, CLUSTER
  • SAVE, BGSAVE, BGREWRITEAOF
  • FLUSHDB, FLUSHALL
  • PUBSUB, SLOWLOG, CLIENT
  • PING, RANDOMKEY, DBSIZE

セッションハンドラとしての利用

PHP のセッション情報を Redis クラスタに保存することができる。設定は php.ini または実行時に行う。
session.save_handler = rediscluster
session.save_path = "seed[]=10.0.0.1:7000&seed[]=10.0.0.2:7000&timeout=2.5&read_timeout=2&failover=error&persistent=1"

save_path のパラメータ説明

  • seed[]: クラスタ接続用のシードノード(必須)
  • timeout: 接続および書き込み時のタイムアウト(秒)
  • read_timeout: 読み取り時のタイムアウト
  • persistent: 1で永続接続を使用
  • failover:
    • none: マスタのみ使用
    • error: マスタ障害時にスレーブから読み取り
  • distribute: 読み取りをマスタ/スレーブ間でランダム分散(ロードバランス)

タグ: Redis Cluster phpredis PHPセッション 高可用性 NoSQL

6月10日 22:04 投稿