Redisの核心技術とキャッシュ問題の本質的理解

以下は、Redisに関する代表的な技術課題とその本質を整理した内容です。

1. Redisをキャッシュとして選ぶ理由

Redisは単なるキャッシュではなく、多機能なインメモリデータストアです。主な利点は以下の通りです:

  • 豊富なデータ構造:String、Hash、List、Set、Sorted Setに加え、HyperLogLog、Geo、Bloom Filter(RedisBloomモジュール)などもサポート。
  • 永続化機能:RDBスナップショットおよびAOFログによるデータ保護が可能。
  • 柔軟なエビクションポリシー:LRU、LFU、TTLベースなどの戦略でメモリ管理が可能。
  • 拡張性:Redis ClusterやSentinelによる高可用性・スケーラビリティを実現。

2. シングルスレッドでも高速な理由

RedisはI/O処理とコマンド実行を分離し、非ブロッキングI/Oとイベントループ(epoll/kqueue)を活用しています。さらに、内部データ構造(例:ziplist、skiplist)が高度に最適化されており、CPUバウンドではなくI/Oバウンドのワークロードに最適です。

3. 主要データ構造とユースケース

データ型用途例
Stringセッションキャッシュ、カウンタ(INCR)、分散ロック(SETNX)
Hashユーザー情報などのオブジェクト格納(HGETALL)
Listメッセージキュー(LPUSH/RPOP)、最新N件取得
Setタグ管理、共通友達検索(SINTER)
Sorted Setランキング(ZADD/ZRANGE)、遅延タスク
HyperLogLogユニークビジター(UV)推定(PFADD/PFCOUNT)
Geo近隣検索(GEORADIUS)

4. キー管理の内部構造

Redisはグローバルなハッシュテーブル(dict)でキーを管理し、O(1)での検索を実現しています。再ハッシュ(rehash)は「漸進的」に行われ、長時間のブロッキングを回避します。

5. パイプライン(Pipeline)の効果

ネットワーク往復時間(RTT)を削減するために、複数コマンドを一括送信できます。例:

// 通常: 10,000回のSET → 10,000 RTT
// Pipeline: 10,000回のSET → 1 RTT + 実行時間

6. Windows版が公式提供されない理由

Linux/Unix系OSのI/Oモデル(epoll)やプロセス管理がRedisのパフォーマンス要件に最適であり、Windows対応のメンテナンスコストが高いためです。

7. 永続化方式:RDB vs AOF

  • RDB:コンパクトなバイナリスナップショット。復旧が高速だが、最後のスナップショット以降のデータが失われる可能性あり。
  • AOF:書き込み操作をログ形式で記録。データ損失が少ないが、ファイルサイズが大きく、復旧に時間がかかる。

運用では両方を併用(AOF + RDB)することも可能です。

8. Redisトランザクションの制限

MULTI/EXECで囲まれたコマンドは直列実行されますが、ロールバック機能はありません。構文エラー時は全体がキャンセルされますが、実行時のエラー(例:型不一致)は無視されます。

9. Redis 6.0のマルチスレッド導入理由

ネットワークI/O(読み取り/書き込み)を複数スレッドで並列処理し、メインスレッドの負荷を軽減します。ただし、コマンドの実行自体は依然としてシングルスレッドで行われます。

10. 大規模URL存在チェック

100億件のURLから存在判定を行うには、Bloom Filterが最適です。誤検知(false positive)はあるものの、メモリ使用量を劇的に削減できます。

11. 漸進的rehashの仕組み

ハッシュテーブルの拡張時、すべてのエントリを一度に移動せず、1回の操作ごとに一部のスロットを移行します。これにより、長時間のブロッキングを回避します。

12. キーの有効期限管理戦略

  • 即時削除:過期時に即削除(CPU負荷高)
  • 遅延削除:アクセス時にチェック(メモリ浪費のリスク)
  • 定期削除:ランダムサンプリングで過期キーを削除(バランス型)

13. メモリエビクションポリシー

設定可能なポリシーには以下があります:

  • allkeys-lru:全キー対象のLRU
  • volatile-lfu:有効期限付きキー対象のLFU
  • volatile-ttl:TTLが短いキーを優先削除

14. BigKeyのリスク

1つのキーが10KB以上(特に1MB超)の場合、操作がブロッキングされ、ネットワーク帯域を圧迫します。監査コマンド(MEMORY USAGE key)で検出可能です。

15. キャッシュ障害の本質

「キャッシュミスによるDBへの集中アクセス」という共通の根幹問題があります:

  • キャッシュブレイク(Cache Break):ホットキーの突然の消失
  • キャッシュペンネトレーション(Penetration):存在しないキーへの継続的クエリ
  • キャッシュ雪崩(Snowflake):大規模なキャッシュ同時失効またはサービス停止

根本的対策

  1. ホットキー保護:永続化 or TTL延長(「ウォッチドッグ」方式)
  2. 存在しないキー対策:Bloom Filterで事前フィルタリング
  3. DB保護:リクエストのキューイング、サーキットブレーカー、接続プール制限

実証実験:キャッシュ障害の影響

以下のようなJavaコードで、キャッシュ無効後に1500スレッドがMySQLに集中アクセスすると、接続数上限(デフォルト151)を超えてDBがダウンします:

public class CacheFailureSimulator {
    static volatile boolean useCache = true;

    public static void main(String[] args) throws Exception {
        // 1分後にキャッシュ無効化
        new Timer().schedule(new TimerTask() {
            @Override public void run() { useCache = false; }
        }, 60_000);

        while (true) {
            ExecutorService es = Executors.newFixedThreadPool(1500);
            for (int i = 0; i < 1500; i++) {
                es.submit(() -> {
                    if (!useCache) {
                        // DBクエリ(接続確立)
                        try (Connection c = DriverManager.getConnection(...)) {
                            Thread.sleep(3000); // シミュレーション遅延
                        } catch (Exception e) { /* 接続拒否発生 */ }
                    }
                });
            }
        }
    }
}

この実験から、キャッシュ層の信頼性とDB側のガードレール設計の重要性が明らかになります。

タグ: redis キャッシュ データ構造 高可用性 パフォーマンスチューニング

6月2日 19:01 投稿