Skip to content
Published on

Redisクラスターアーキテクチャと高可用性運用ガイド: Sentinel·Clusterモード·メモリ最適化·障害復旧

Authors
  • Name
    Twitter
Redis Cluster Architecture

はじめに

Redisはインメモリデータストアとして、キャッシュ、セッションストア、メッセージブローカーなど様々な用途で使用されている。単一インスタンスでの運用開始は容易だが、本番環境で高可用性(HA)と水平スケーリングを実現するには、SentinelまたはClusterモードの十分な理解が不可欠である。メモリベースのシステムであるため、メモリ管理、退去ポリシー、永続化戦略、そして障害時の復旧手順まで、運用全般を網羅する知識が必要となる。

本記事では、Redisの3つのデプロイモード(Standalone、Sentinel、Cluster)の比較から始め、Sentinelのクォーラムメカニズム、Clusterのハッシュスロット分散、レプリケーションプロトコル(PSYNC)、メモリ最適化戦略、永続化(RDB/AOF)、スローログ分析、そして代表的な障害シナリオ(Split-brain、OOM)と復旧手順を実践例とともに解説する。

Redisデプロイモード比較: Standalone vs Sentinel vs Cluster

デプロイモード比較表

項目StandaloneSentinelCluster
ノード数1(+ オプションのレプリカ)最小3 Sentinel + 1 Master + N Replica最小6(3 Master + 3 Replica)
データ分散不可不可(単一マスター)ハッシュスロットベースの自動分散
自動フェイルオーバー不可可能(クォーラムベース)可能(過半数投票)
書き込みスケーリング不可不可可能(マルチマスター)
読み取りスケーリングレプリカREADONLYレプリカREADONLYレプリカREADONLY
クライアント複雑度低い中程度(Sentinel対応)高い(MOVED/ASKリダイレクション)
適用シナリオ開発/小規模HA必要な単一データセット大規模データ + HA

選択基準

# 意思決定フロー
# 1. データが単一ノードのメモリに収まるか?
#    - YES → Sentinel(HA必要時)またはStandalone
#    - NO  → Cluster必須
#
# 2. 書き込み性能のスケーリングが必要か?
#    - YES → Cluster(マルチマスター)
#    - NO  → Sentinelで十分
#
# 3. 運用の複雑さを許容できるか?
#    - 小規模チーム → Sentinelを優先検討
#    - 専任インフラチーム → Cluster導入可能

Sentinelアーキテクチャとクォーラムメカニズム

Sentinelの役割

Redis Sentinelは、Redisインスタンスを監視し、マスター障害時に自動的にレプリカを昇格させる分散監視システムである。Sentinelプロセス自体も分散されており、単一障害点(SPOF)を防止する。

# Sentinel設定ファイル (sentinel.conf)
port 26379
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

# sentinel monitor <master-name> <ip> <port> <quorum>
# quorum = 障害判定に必要な最小Sentinel合意数

クォーラム(Quorum)と過半数(Majority)

クォーラムと過半数は異なる概念である。クォーラムは障害を検知するために必要な最小合意数であり、過半数は実際のフェイルオーバーを実行するための要件である。

# 3台Sentinel構成の例
# quorum = 2(2台が同意すればODOWN判定)
# majority = 2(3台中2台 = 過半数)

# 5台Sentinel構成の例
# quorum = 3(3台が同意すればODOWN判定)
# majority = 3(5台中3台 = 過半数)

# Sentinelステータス確認
redis-cli -p 26379 SENTINEL masters
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
redis-cli -p 26379 SENTINEL replicas mymaster

フェイルオーバーの順序

# 1. SDOWN (Subjective Down) - 個別Sentinelがマスター無応答を検知
#    down-after-milliseconds経過後に発生

# 2. ODOWN (Objective Down) - quorum数以上のSentinelがSDOWNに同意
#    この時点でフェイルオーバープロセス開始

# 3. Leader Election - Sentinel中の1つがリーダーに選出
#    majority以上の投票が必要(Raft類似アルゴリズム)

# 4. Replica選択 - リーダーSentinelが最適なレプリカを選択
#    優先順位: replica-priority → レプリケーションオフセット → runid

# 5. Failover実行
#    選択されたレプリカにSLAVEOF NO ONEコマンド
#    他のレプリカを新マスターに再接続

# フェイルオーバー進行状況監視
redis-cli -p 26379 SENTINEL failover-status mymaster

Clusterハッシュスロットとリシャーディング

ハッシュスロット分配

Redis Clusterはキースペース全体を16384個のハッシュスロットに分割して管理する。各マスターノードはスロットの一部を担当し、キーのCRC16ハッシュ値を16384で割った余りでスロットが決定される。

# クラスター作成(最小6ノード: 3 Master + 3 Replica)
redis-cli --cluster create \
  192.168.1.10:7000 192.168.1.11:7001 192.168.1.12:7002 \
  192.168.1.10:7003 192.168.1.11:7004 192.168.1.12:7005 \
  --cluster-replicas 1

# スロット分配確認
redis-cli -c -h 192.168.1.10 -p 7000 CLUSTER SLOTS

# 個別キーのスロット確認
redis-cli -c CLUSTER KEYSLOT mykey
# (integer) 14687

# ハッシュタグを使用して同一スロットに配置
# 波括弧内の文字列のみでスロットが決定される
redis-cli -c SET "user:1001:profile" "data1"
redis-cli -c SET "user:1001:session" "data2"
# 上記2つのキーは異なるスロットに配置される可能性がある

# ハッシュタグ使用時は同一スロットに配置可能
# CLUSTER KEYSLOTコマンドで確認
redis-cli CLUSTER KEYSLOT "user:1001"

リシャーディング(Resharding)

# オンラインリシャーディング - サービス中断なしでスロット移動
redis-cli --cluster reshard 192.168.1.10:7000 \
  --cluster-from <source-node-id> \
  --cluster-to <target-node-id> \
  --cluster-slots 1000 \
  --cluster-yes

# リシャーディング進行状況確認
redis-cli -c -h 192.168.1.10 -p 7000 CLUSTER INFO

# クラスターヘルスチェック
redis-cli --cluster check 192.168.1.10:7000

# スロットリバランシング(自動均等分配)
redis-cli --cluster rebalance 192.168.1.10:7000 \
  --cluster-threshold 2

MOVEDとASKリダイレクション

# Python redis-py-cluster例
import redis

# ClusterModeクライアントはMOVED/ASKを自動処理
rc = redis.RedisCluster(
    host='192.168.1.10',
    port=7000,
    decode_responses=True,
    skip_full_coverage_check=True
)

# 通常の使用 - リダイレクション自動処理
rc.set('session:abc123', 'user_data')
value = rc.get('session:abc123')

# パイプライン使用時は同一スロットのキーのみバッチ可能
# ハッシュタグを活用すれば同一スロットに配置可能
pipe = rc.pipeline()
pipe.set('order:1001:status', 'pending')
pipe.set('order:1001:total', '50000')
pipe.execute()

# MOVED: キーが別ノードにある場合(永久移動)
# ASK: リシャーディング中に一時的に別ノードにある場合

レプリケーション(Replication)とPSYNC

レプリケーション設定とPSYNCプロトコル

# レプリカ設定
# redis.conf(レプリカノード)
replicaof 192.168.1.10 6379
masterauth your_password

# PSYNCプロトコル動作:
# 1. Full Sync(全体同期)
#    - レプリカが初めて接続された場合またはbacklogが不足している場合
#    - マスターがRDBスナップショットを生成して送信
#    - 転送中の変更分は別バッファに保存後追加送信

# 2. Partial Sync(部分同期) - PSYNC2
#    - レプリカが短い断絶後に再接続した場合
#    - replication backlogに保存された差分のみ送信
#    - はるかに高速で効率的

# backlogサイズ設定(デフォルト1MB、本番環境では増加推奨)
repl-backlog-size 256mb
repl-backlog-ttl 3600

# レプリケーション状態確認
redis-cli INFO replication

レプリケーション監視

# マスターでレプリケーション状態確認
redis-cli INFO replication
# role:master
# connected_slaves:2
# slave0:ip=192.168.1.11,port=6379,state=online,offset=1234567,lag=0
# slave1:ip=192.168.1.12,port=6379,state=online,offset=1234567,lag=1
# master_replid:abc123def456...
# master_repl_offset:1234567
# repl_backlog_active:1
# repl_backlog_size:268435456

# レプリケーションラグ監視 - lagが継続的に高い場合は対策が必要
# lag > 10 : ネットワークまたはレプリカ性能確認
# lag > 60 : 緊急対策必要(フルシンク発生の可能性)

# レプリカで読み取り専用確認
redis-cli -h 192.168.1.11 CONFIG GET replica-read-only

メモリ管理と退去ポリシー

maxmemory設定

# maxmemory設定 (redis.conf)
maxmemory 8gb

# ランタイム変更
redis-cli CONFIG SET maxmemory 8589934592

# 現在のメモリ使用量確認
redis-cli INFO memory
# used_memory:4294967296
# used_memory_human:4.00G
# used_memory_rss:4831838208
# used_memory_rss_human:4.50G
# mem_fragmentation_ratio:1.12
# maxmemory:8589934592
# maxmemory_human:8.00G
# maxmemory_policy:allkeys-lru

退去ポリシー比較

ポリシー対象キーアルゴリズム適用シナリオ
noevictionなし(書き込み拒否)-データ損失不可
allkeys-lru全キーLRU一般キャッシュ(最も一般的)
allkeys-lfu全キーLFU人気度ベースのキャッシュ
allkeys-random全キーランダム均等アクセスパターン
volatile-lruTTL設定キーのみLRUキャッシュ + 永続データ混合
volatile-lfuTTL設定キーのみLFUTTLキー中の頻度ベース削除
volatile-ttlTTL設定キーのみ残りTTL短い順期限切れ間近のキー優先削除
volatile-randomTTL設定キーのみランダムTTLキーランダム削除
# 退去ポリシー設定
redis-cli CONFIG SET maxmemory-policy allkeys-lfu

# LFUカウンター設定(Redis 4.0+)
# lfu-log-factor: カウンター増加速度(デフォルト10、高いほど遅く増加)
# lfu-decay-time: カウンター減少周期(分、デフォルト1)
redis-cli CONFIG SET lfu-log-factor 10
redis-cli CONFIG SET lfu-decay-time 1

# 特定キーのLFU頻度確認
redis-cli OBJECT FREQ mykey

# 退去統計確認
redis-cli INFO stats | grep evicted
# evicted_keys:12345

メモリ最適化技法

# 1. データ構造最適化 - ziplist/listpack活用
# 小規模ハッシュにziplist使用(メモリ最大10倍節約)
redis-cli CONFIG SET hash-max-ziplist-entries 128
redis-cli CONFIG SET hash-max-ziplist-value 64

# 小規模リストにlistpack使用(Redis 7.0+)
redis-cli CONFIG SET list-max-listpack-size -2

# 小規模Sorted Setにziplist使用
redis-cli CONFIG SET zset-max-ziplist-entries 128
redis-cli CONFIG SET zset-max-ziplist-value 64

# 2. キー命名最適化 - 短いキー名使用
# Bad:  user:session:authentication:token:1001
# Good: u:s:t:1001

# 3. メモリ使用量分析
redis-cli MEMORY USAGE mykey
redis-cli MEMORY DOCTOR

# 4. 大きなキー(Big Key)検出
redis-cli --bigkeys --memkeys

# 5. Lazy Free設定(バックグラウンド削除でブロッキング防止)
redis-cli CONFIG SET lazyfree-lazy-eviction yes
redis-cli CONFIG SET lazyfree-lazy-expire yes
redis-cli CONFIG SET lazyfree-lazy-server-del yes

永続化: RDBとAOF

RDB vs AOF比較

項目RDB(Snapshot)AOF(Append Only File)
方式特定時点の全体スナップショット全書き込みコマンドの順次記録
データ損失最後のスナップショット以降損失可能fsync設定により最小化
ファイルサイズ小(バイナリ圧縮)大(コマンドテキスト、rewriteで縮小)
復旧速度高速低速(コマンド再実行)
性能影響fork()時の瞬間的遅延fsync頻度に応じた継続的影響
推奨用途バックアップデータ安全性
# RDB設定 (redis.conf)
save 900 1      # 900秒(15分)内1個以上変更時スナップショット
save 300 10     # 300秒(5分)内10個以上変更時スナップショット
save 60 10000   # 60秒(1分)内10000個以上変更時スナップショット
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis

# AOF設定
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec  # 推奨: 1秒ごとにfsync(性能と安全性のバランス)
# appendfsync always  # 毎コマンドfsync(最も安全、性能低下)
# appendfsync no      # OSに委任(最速だがデータ損失リスク)

# AOF Rewrite設定
auto-aof-rewrite-percentage 100  # AOFが前回rewrite比100%大きくなったら
auto-aof-rewrite-min-size 64mb   # 最小64MB以上の場合にrewrite実行

# 本番推奨: RDB + AOF同時使用
# RDB: 高速復旧 + バックアップ
# AOF: データ損失最小化

AOF再書き込みと管理

# 手動AOF Rewrite実行
redis-cli BGREWRITEAOF

# AOFステータス確認
redis-cli INFO persistence
# aof_enabled:1
# aof_rewrite_in_progress:0
# aof_last_rewrite_time_sec:2
# aof_current_size:134217728
# aof_base_size:67108864

# AOF整合性検査
redis-check-aof --fix appendonly.aof

# RDB整合性検査
redis-check-rdb dump.rdb

# 手動バックアップ(BGSAVE使用)
redis-cli BGSAVE
# バックグラウンドでRDBスナップショット生成
# dump.rdbファイルを安全なストレージにコピー

スローログ分析

# スローログ設定
redis-cli CONFIG SET slowlog-log-slower-than 10000  # 10ms以上を記録
redis-cli CONFIG SET slowlog-max-len 128            # 最大128項目保持

# スローログ照会
redis-cli SLOWLOG GET 10
# 1) 1) (integer) 14          # ログID
#    2) (integer) 1710230400   # タイムスタンプ
#    3) (integer) 15230       # 実行時間(マイクロ秒)
#    4) 1) "KEYS"             # コマンド
#       2) "*session*"
#    5) "192.168.1.50:54321"  # クライアントアドレス
#    6) ""

# スローログ統計
redis-cli SLOWLOG LEN
redis-cli SLOWLOG RESET

# 本番環境で必ず避けるべきO(N)コマンド:
# KEYS *        → SCANで代替
# SMEMBERS      → SSCANで代替
# HGETALL       → HSCANで代替
# LRANGE 0 -1   → ページネーション適用

SCANベースの安全なキー探索

# KEYSの代わりにSCAN使用(ノンブロッキング)
redis-cli SCAN 0 MATCH "session:*" COUNT 100
# 1) "17920"    # 次のカーソル
# 2) 1) "session:abc123"
#    2) "session:def456"
#    ...

# カーソルが0になるまで繰り返しスキャン
redis-cli SCAN 17920 MATCH "session:*" COUNT 100

メモリフラグメンテーション対応

# フラグメンテーション比率確認
redis-cli INFO memory | grep frag
# mem_fragmentation_ratio:1.45
# mem_fragmentation_bytes:536870912

# mem_fragmentation_ratioの解釈:
# 1.0 ~ 1.5 : 正常範囲
# 1.5以上   : 深刻なフラグメンテーション、対策が必要
# 1.0未満   : スワップ使用中(非常に危険)

# Active Defragmentation有効化(Redis 4.0+)
redis-cli CONFIG SET activedefrag yes
redis-cli CONFIG SET active-defrag-enabled yes

# フラグメンテーション閾値設定
redis-cli CONFIG SET active-defrag-ignore-bytes 100mb
redis-cli CONFIG SET active-defrag-threshold-lower 10   # 10%以上で開始
redis-cli CONFIG SET active-defrag-threshold-upper 100  # 100%以上で最大努力
redis-cli CONFIG SET active-defrag-cycle-min 1           # CPU最小1%使用
redis-cli CONFIG SET active-defrag-cycle-max 25          # CPU最大25%使用

# jemalloc統計確認
redis-cli MEMORY MALLOC-STATS

障害シナリオと復旧手順

シナリオ1: Split-brain(スプリットブレイン)

ネットワークパーティションが発生すると、Sentinelクラスターが分裂し、2つのマスターが同時に存在する可能性がある。これはデータ不整合を引き起こす最も危険な障害である。

# Split-brain予防設定
# マスターが最小N個のレプリカと接続されていない場合書き込み拒否
redis-cli CONFIG SET min-replicas-to-write 1
redis-cli CONFIG SET min-replicas-max-lag 10

# 発生時の復旧手順:
# 1. 全Redisインスタンスの状態確認
redis-cli -h 192.168.1.10 -p 6379 INFO replication
redis-cli -h 192.168.1.11 -p 6379 INFO replication

# 2. どのマスターが最新データを持っているか確認
redis-cli -h 192.168.1.10 -p 6379 INFO replication | grep master_repl_offset
redis-cli -h 192.168.1.11 -p 6379 INFO replication | grep master_repl_offset

# 3. 古いマスターをレプリカに降格
redis-cli -h 192.168.1.11 -p 6379 REPLICAOF 192.168.1.10 6379

# 4. Sentinel状態リセット
redis-cli -p 26379 SENTINEL RESET mymaster

# 5. データ整合性検証
redis-cli -h 192.168.1.10 DBSIZE
redis-cli -h 192.168.1.11 DBSIZE

シナリオ2: OOM(Out of Memory)

# OOM予防監視
redis-cli INFO memory
# used_memory_peak:8589934592
# used_memory_peak_human:8.00G

# OOM Killerによる終了確認
# システムログ確認
# dmesgまたは/var/log/syslogでOOM関連ログを確認

# 緊急復旧手順:
# 1. maxmemory確認および調整
redis-cli CONFIG SET maxmemory 12gb

# 2. 退去ポリシーがnoevictionの場合変更
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 3. 大きなキーの特定とクリーンアップ
redis-cli --bigkeys
redis-cli --memkeys --memkeys-samples 100

# 4. 不要なキーに有効期限設定
redis-cli SCAN 0 MATCH "temp:*" COUNT 1000
# 必要なキーにTTL設定
redis-cli EXPIRE "temp:old-data" 3600

# 5. 今後の予防のためアラート設定(maxmemoryの80%で警告)

シナリオ3: Clusterノード障害

# クラスター状態確認
redis-cli -c CLUSTER INFO
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_slots_pfail:0
# cluster_slots_fail:0

# 障害ノード確認
redis-cli -c CLUSTER NODES
# 障害ノードに"fail"フラグが表示される

# ノード交換手順:
# 1. 新しいRedisインスタンス起動
redis-server /etc/redis/7006.conf

# 2. クラスターに新ノード追加
redis-cli --cluster add-node 192.168.1.13:7006 192.168.1.10:7000

# 3. 新ノードを特定マスターのレプリカに指定
redis-cli --cluster add-node 192.168.1.13:7006 192.168.1.10:7000 \
  --cluster-slave --cluster-master-id <master-node-id>

# 4. 障害ノード削除
redis-cli --cluster del-node 192.168.1.10:7000 <failed-node-id>

# 5. スロット再割当(マスター障害時)
redis-cli --cluster fix 192.168.1.10:7000

運用監視と主要メトリクス

# 総合状態確認スクリプト
redis-cli INFO ALL | grep -E "used_memory_human|mem_fragmentation_ratio|connected_clients|blocked_clients|instantaneous_ops_per_sec|hit_rate|evicted_keys|keyspace_misses"

# 主要監視メトリクス:
# 1. メモリ: used_memory / maxmemory比率
# 2. フラグメンテーション: mem_fragmentation_ratio
# 3. キャッシュヒット率: keyspace_hits / (keyspace_hits + keyspace_misses)
# 4. 接続数: connected_clients
# 5. スループット: instantaneous_ops_per_sec
# 6. 退去数: evicted_keys(急増時に警告)
# 7. レプリケーションラグ: master_repl_offset vs slave_repl_offset

# レイテンシ監視
redis-cli --latency
redis-cli --latency-history

# レイテンシイベント監視(Redis 2.8.13+)
redis-cli CONFIG SET latency-monitor-threshold 100
redis-cli LATENCY LATEST
redis-cli LATENCY HISTORY event-name

運用時の注意事項

  1. メモリオーバーコミット: Linuxでvm.overcommit_memory=1設定が必要である。fork()ベースのRDB/AOF rewrite時にメモリ不足で失敗する可能性がある。

  2. Transparent Huge Pages(THP)無効化: THPはRedisのfork性能に悪影響を及ぼすため、必ず無効化する。

  3. maxmemoryマージン: 物理メモリの70-80%に設定し、レプリケーションバッファ、AOF rewrite、OSキャッシュなどに余裕を持たせる。

  4. ClusterモードでのMULTI/EXEC: トランザクション内の全キーが同じスロットに存在する必要がある。ハッシュタグを活用すること。

  5. Sentinel配置場所: SentinelインスタンスをRedisノードとは異なる物理サーバーまたはアベイラビリティゾーンに配置し、同時障害を防止する。

  6. KEYSコマンド禁止: 本番環境でKEYSコマンドはO(N)ブロッキングを引き起こす。SCANで代替し、rename-commandで無効化すること。

# 本番環境カーネルチューニング
# /etc/sysctl.conf
# vm.overcommit_memory = 1
# net.core.somaxconn = 65535
# net.ipv4.tcp_max_syn_backlog = 65535

# THP無効化
# echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 危険コマンド無効化 (redis.conf)
# rename-command KEYS ""
# rename-command FLUSHDB ""
# rename-command FLUSHALL ""
# rename-command DEBUG ""

まとめ

Redisの高可用性運用は、単にSentinelやClusterを設定するだけにとどまらない。メモリ管理、退去ポリシー、永続化戦略、レプリケーション監視、そして障害シナリオへの事前準備が全て揃って初めて、安定した本番運用が可能となる。

核心は3つである。第一に、ワークロードに合ったデプロイモードを選択すること。第二に、メモリ上限と退去ポリシーを必ず設定すること。第三に、障害シナリオごとの復旧手順を文書化し、定期的に訓練すること。この3つが揃えば、Redisは超高速インメモリストアとしての強みを安定的に発揮できる。

参考資料