以下は、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:全キー対象のLRUvolatile-lfu:有効期限付きキー対象のLFUvolatile-ttl:TTLが短いキーを優先削除
14. BigKeyのリスク
1つのキーが10KB以上(特に1MB超)の場合、操作がブロッキングされ、ネットワーク帯域を圧迫します。監査コマンド(MEMORY USAGE key)で検出可能です。
15. キャッシュ障害の本質
「キャッシュミスによるDBへの集中アクセス」という共通の根幹問題があります:
- キャッシュブレイク(Cache Break):ホットキーの突然の消失
- キャッシュペンネトレーション(Penetration):存在しないキーへの継続的クエリ
- キャッシュ雪崩(Snowflake):大規模なキャッシュ同時失効またはサービス停止
根本的対策
- ホットキー保護:永続化 or TTL延長(「ウォッチドッグ」方式)
- 存在しないキー対策:Bloom Filterで事前フィルタリング
- 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側のガードレール設計の重要性が明らかになります。