Redisキースペースの理解:削除と検査コマンド

この包括的なガイドでRedisキースペース管理の力を解き放ちましょう。`SCAN`を使用してデータを安全に検査する方法(本番環境で`KEYS`を避ける理由)と、`DEL`およびノンブロッキングな`UNLINK`を使用して効率的にキーを削除する方法を学びます。`FLUSHDB`と`FLUSHALL`の破壊的な性質を理解し、健全で高性能なRedisインスタンスを維持するためのベストプラクティスを発見しましょう。

Redisキースペースの理解:削除と検査コマンド

Redisキースペースは、現在選択されているデータベース内のキーのセットにすぎませんが、これらのキーを検査および削除する方法によって、クリーンアップが退屈なものになるか、トラフィックの最中にアプリケーションが停止するかが決まります。

ほとんどのチームはこれを苦労して学びます。誰かがステージングキャッシュからsession:*キーを削除する必要があり、KEYS session:*を実行してリストを確認し、その後本番環境で同じ習慣を試します。小さなデータベースでは問題ないように感じられます。何百万ものキーがあるビジーなインスタンスでは、コマンドがサーバーを十分に長く保持し、無関係なリクエストがその背後でキューイングされる可能性があります。Redisはコマンドを非常に迅速に処理しますが、キースペース全体をウォークするコマンドは依然としてその作業を行う必要があります。

日常的な操作では、2つの別々のステップと考えてください:キーを安全に見つけ、次に安全に削除します。キーの検査を無害な読み取りとして扱わないでください。読み取りコマンドでもコストがかかる可能性があります。

実際に使用しているデータベースから始める

何かを削除する前に、エンドポイントと論理データベースを確認します。

redis-cli -h redis.example.internal -p 6379 INFO keyspace

出力は次のようになります:

# Keyspace
db0:keys=154233,expires=129900,avg_ttl=2851412
db2:keys=32,expires=32,avg_ttl=60000

これにより、どの論理データベースにキーが含まれているかがわかります。多くのRedisデプロイメントはデータベース0のみを使用します。Redis Clusterはデータベース0のみをサポートするため、FLUSHDBFLUSHALLはそこで特別な注意が必要です。通常の「選択されたデータベース」のメンタルモデルは同じようには機能しないからです。

アプリケーションがスタンドアロンRedisで番号付きデータベースを使用する場合は、明示的に正しいものを選択します:

redis-cli -n 2 DBSIZE

DBSIZEは選択されたデータベース内のキーの数を返します。名前は表示されず、検査パスを置き換えることもありませんが、クリーンアップの前後に適切な健全性チェックとなります。

キースペースが小さく使い捨て可能な場合のみKEYSを使用する

KEYS patternは、グロブスタイルのパターンに一致するすべてのキーを返します:

KEYS user:*
KEYS cache:product:???
KEYS *

パターンルールは便利です:*は任意のシーケンスに一致し、?は1文字に一致し、[0-9]などのブラケット範囲は範囲内の1文字に一致します。

問題は正確さではありません。問題は、KEYSが1つのコマンドでキースペース全体をスキャンすることです。数百のキーを持つローカル開発Redisでは便利です。共有キャッシュでは、Redisが結果を生成している間に他のすべてのクライアントにレイテンシが追加される可能性があります。このコマンドは線形の複雑さを持つキースペースコマンドとして文書化されているため、通常の本番クリーンアップスクリプトの一部にすべきではありません。

それでも私は2つの場所でKEYSを使用します:

  • テストを書いている間の使い捨てのローカルRedis。
  • すでにDBSIZEを確認し、コマンドが驚かせないことがわかっている小さなステージングデータベース。

他の場所では、SCANを使用します。

本番検査にはSCANを使用する

SCANはカーソルベースです:

SCAN 0 MATCH user:* COUNT 100

Redisは2つのものを返します:次のカーソルとキーのバッチです。カーソル0から始めます。返されたカーソルでスキャンを続けます。Redisが再びカーソル0を返したら停止します。

1) "24576"
2) 1) "user:100"
   2) "user:101"

COUNTはヒントであり、約束ではありません。Redisは特定の反復でより多くのキー、より少ないキー、またはキーをまったく返さない場合があります。MATCHは返されるものをフィルタリングしますが、Redisは依然としてキースペースを進みます。狭いパターンはクライアント側の作業を減らすのに役立ちますが、すべてのスキャンを魔法のように無料にするわけではありません。

シェル作業では、カーソルループを隠すためredis-cli --scanを優先します:

redis-cli --scan --pattern 'session:*'

すべてを印刷せずに一致するキーをカウントするには:

redis-cli --scan --pattern 'session:*' | wc -l

削除前にタイプを検査するには:

redis-cli --scan --pattern 'session:*' | head
redis-cli TYPE session:abc123
redis-cli TTL session:abc123
redis-cli MEMORY USAGE session:abc123

TTLはクリーンアップの決定に特に役立ちます。キャッシュ名前空間にすでに適切な有効期限がある場合、一括削除はまったく必要ないかもしれません。キーを自然に期限切れにさせることは、通常、営業時間中に大きな削除を強制するよりもリスクが低いです。

既知のキーをDELで削除する

DELは1つ以上のキーを削除し、存在した数を返します:

DEL session:abc123
DEL session:abc123 session:def456 session:ghi789

小さなキーの場合、DELは通常問題ありません。文字列キーや小さなハッシュを削除するのは怖いケースではありません。痛いのは、多数の要素を持つリスト、インデックスとして使用されるセット、元の目的をはるかに超えて成長したハッシュなど、大きな集約値を削除する場合です。Redisはキースペースからキーを削除しますが、大きな値を解放するにはメインパスで時間がかかる可能性があります。

小さなキーを1つ削除することがわかっている場合は、DELを使用します。多くのキーを削除する場合や、それらがどれだけ大きいかわからない場合は、UNLINKを使用します。

大規模または一括削除にはUNLINKを優先する

UNLINKDELと同じ形状を持ちます:

UNLINK session:abc123
UNLINK cache:old:1 cache:old:2 cache:old:3

重要な違いはメモリ解放です。UNLINKはキーをキースペースから即座に削除し、その後非同期にメモリを解放します。これにより、大きな値を保持する可能性のあるキーをクリーンアップする際のより安全なデフォルトになります。

これはUNLINKが魔法であることを意味しません。スクリプトが可能な限り迅速に何百万ものキーを発見してアンリンクすると、依然としてプレッシャーが発生する可能性があります。Redisは依然としてコマンドを処理する必要があり、レプリカは変更を受け取る必要があり、メモリは解放される必要があります。実行中にレイテンシとメモリを監視できるように、一括クリーンアップをスロットルします。

実用的なクリーンアップループは次のようになります:

redis-cli --scan --pattern 'session:*' |
  xargs -r -L 100 redis-cli UNLINK

これにより、100キーのバッチで削除されます。環境に合わせてバッチサイズを調整します。静かなメンテナンスウィンドウでは、より大きなバッチを使用するかもしれません。ユーザー向けトラフィックで共有されるホットキャッシュでは、より小さなバッチとバッチ間の短いスリープの方が親切かもしれません:

redis-cli --scan --pattern 'session:*' |
while read -r key; do
  redis-cli UNLINK "$key" >/dev/null
  sleep 0.005
done

このバージョンは遅いですが、停止が容易で、理解しやすいです。

パターン削除に注意する

Redisは意図的にDEL user:*を提供していません。スキャンと削除を自分で組み合わせる必要があります。この摩擦は、パターン削除で事故が発生するため有用です。

削除前に:

redis-cli --scan --pattern 'user:*' | head -50
redis-cli --scan --pattern 'user:*' | wc -l

最初のサンプルを見てください。ターゲットサイズをカウントします。期待されるクリーンアップが「数千の放棄されたセッション」で、カウントが「データベースのほとんど」である場合、停止してパターンを修正します。

クリーンアップを退屈にする命名規則を使用します:

app:prod:session:<id>
app:prod:rate-limit:<user-id>
app:prod:cache:product:<id>

これはsession:<id>よりも冗長ですが、名前空間を正確にターゲットにできます。Redis Clusterでは、キー名にはスロット配置のためのcart:{user123}:itemsなどのハッシュタグが含まれる場合があります。広範なパターンを書く前に、これらの規則に注意してください。

FLUSHDBとFLUSHALLはリセットボタン

FLUSHDBは選択されたデータベースからすべてのキーを削除します:

FLUSHDB
FLUSHDB ASYNC

FLUSHALLはすべての論理データベースからすべてのキーを削除します:

FLUSHALL
FLUSHALL ASYNC

最新のRedisは、フラッシュコマンドにASYNCおよびSYNC修飾子をサポートしています。ASYNCはバックグラウンドで解放をスケジュールし、大きな同期メモリ解放の一時停止を回避するのに役立ちます。操作を元に戻せるわけではありません。キーがキースペースから消えると、アプリケーションはそれらが消えたと見なします。

どちらかのコマンドを使用する前に、3つのチェックを行いたいです:

redis-cli ROLE
redis-cli INFO keyspace
redis-cli CONFIG GET dir

ROLEは、プライマリまたはレプリカに接続しているかを確認するのに役立ちます。INFO keyspaceは影響を受けるものを示します。CONFIG GET dirは、データディレクトリが環境固有のパスを含む傾向があるため、どのインスタンスにいるかについての別のヒントを提供することがよくあります。

開発リセットスクリプトでは、明示的にします:

redis-cli -h 127.0.0.1 -p 6379 -n 0 FLUSHDB ASYNC

デフォルトでredis-cli FLUSHALLを実行するスクリプトは避けてください。デフォルトは、スクリプトが別のホスト、別のコンテナ、または異なる環境変数を持つCIランナーで実行されると変更されます。

削除後に確認する

クリーンアップ後、カウントとアプリケーションの動作の両方を確認します:

redis-cli --scan --pattern 'session:*' | wc -l
redis-cli INFO memory
redis-cli INFO stats | grep expired_keys

UNLINKまたは非同期フラッシュの後、メモリは即座に低下しない場合があります。解放はバックグラウンドで行われ、アロケータは再利用のためにメモリを保持する可能性があるからです。これは自動的にリークではありません。used_memory、レイテンシ、およびキーカウントが期待する方向に動いているかを監視します。

本番変更では、実行する前に正確なコマンドとパターンを書き留めてください。安全なRedisクリーンアップは、正しいコマンドだけではありません。それは、正しいインスタンスに対して、サンプリングしたパターンで、結果を監視できるウィンドウ中に実行される正しいコマンドです。

より安全な本番クリーンアップのランブック

実際のクリーンアップでは、コマンドを記憶から入力するワンライナーではなく、小さなランブックに変換するのが好きです。ランブックは凝っている必要はありません。4つの質問に答えるべきです:どのインスタンス、どのパターン、いくつのキー、そしてどのくらいの速さか。

読み取り専用チェックから始めます:

redis-cli -h redis.example.internal -p 6379 ROLE
redis-cli -h redis.example.internal -p 6379 INFO keyspace
redis-cli -h redis.example.internal -p 6379 --scan --pattern 'app:prod:session:*' | head -20
redis-cli -h redis.example.internal -p 6379 --scan --pattern 'app:prod:session:*' | wc -l

次に、いくつかの代表的なキーを検査します:

redis-cli TYPE app:prod:session:sample
redis-cli TTL app:prod:session:sample
redis-cli MEMORY USAGE app:prod:session:sample

サンプルが数分で自然に期限切れになるキーを示している場合、削除する代わりに待ちます。キーにTTLがなく、明らかに放棄されたデータに属している場合、スロットルされたクリーンアップを進めます。レイテンシまたはメモリを監視するターミナルを開いたままにします:

redis-cli --latency
redis-cli INFO memory

共有本番Redisの場合、意図を見失わずに停止できるスクリプトを好みます:

redis-cli --scan --pattern 'app:prod:session:*' |
while read -r key; do
  redis-cli UNLINK "$key" >/dev/null
  sleep 0.002
done

これは最速のバージョンではありません。レイテンシが動いたり、クライアントが不満を言ったり、パターンが予想よりも広かったりした場合に気づく機会を与えるバージョンです。インスタンスが静かでカウントが控えめな場合、xargs -L 100でのバッチ処理は問題ありません。ポイントは、ペースを意図的に選択することです。

もう1つの習慣が役立ちます:インシデントチケットまたはデプロイメントノートに前後のカウントを保存します。「セッションキーを削除しました」だけでは不十分です。「redis-cache-01のdb0からapp:prod:session:*に一致する48,213キーをUNLINKを使用して削除、レイテンシの増加は観測されませんでした」という種類のメモが後で時間を節約します。