Redisの「ERR no such key」エラーをOBJECT ENCODINGとTTLで解決する

intermediate🔴 Redis2026-04-30| Linux (Ubuntu/CentOS)、Docker、またはマネージドRedis (AWS ElastiCache, GCP Memorystore) 上の Redis 5.0, 6.0, 7.0+

Error Message

ERR no such key
#redis#key#ttl#object-encoding#keyspace

問題:さっきまであったキーが消える

これは典型的な「幽霊」現象です。RAMを消費しているキーを探してRedisインスタンスを調査しているとします。SCANを実行してターゲットを特定し、すぐにそのOBJECT ENCODINGTTLを確認しようとします。しかし、期待したメタデータの代わりに、Redisは次のようなエラーを返します。

(error) ERR no such key

数ミリ秒前にはリストにあったはずなので、戸惑うかもしれません。標準的なRedis CLIは通常、存在しないキーに対して(nil)または-2を返します。しかし、Twemproxyのようなライブラリやプロキシは、これらを明示的なERR no such keyに変換することがあります。これは、実行のまさにその瞬間にキーが期限切れになった場合に発生します。

分析:なぜキーが消えたのか

通常、この消失現象の背後には3つの主な原因があります。

1. レースコンディション(TTLの期限切れ)

これが最大の原因です。セッショントークンや一時的なロックなど、有効期限の短い(例:500ms)キーを検査している場合、最初のコマンドと2番目のコマンドの間にキーが期限切れになる可能性があります。Redisは高速ですが、瞬時ではありません。10msのネットワーク遅延であっても、期限切れ間近のキーにとっては致命的です。

2. 見えない文字とエンコーディングの罠

文字列をよく確認してください。ログからキーをコピー&ペーストすると、末尾のスペース、改行、ヌルバイトなどの隠れた文字が含まれてしまうことがあります。Redisにとって、"user:100""user:100\r" は完全に別物です。コードが誤ってキャリッジリターンを付加している場合、標準的な TTL "user:100" コマンドは毎回失敗します。

3. Luaスクリプト内のロジック

Luaスクリプト内で redis.call() を実行すると、Redisはエラーを厳格に処理します。スクリプトがキーの存在を前提としているのに、すでに期限切れになっている場合、スクリプト全体が no such key エラーでクラッシュすることがあります。これは、メタデータ操作の前に存在確認を行わないスクリプトでよく見られる問題です。

クイックフィックス:存在しないキーの処理

CLIでこの事象が発生している場合、「修正」は単にキーがすでに期限切れになったことを受け入れることです。自動化ツールや監視ツールの場合は、より堅牢な作りが必要です。OBJECT ENCODINGを盲目的に実行してはいけません。

メタデータを安全に取得するために、Luaのワンライナーを使用することを検討してください。これにより、チェックと取得の両方が同じアトミックな操作内で行われることが保証されます。

EVAL "if redis.call('EXISTS', KEYS[1]) == 1 then return redis.call('OBJECT', 'ENCODING', KEYS[1]) else return nil end" 1 my_key

TTLについては、戻り値の -2 はキーが存在しないことを意味することを覚えておいてください。使用しているライブラリが -2 を返す代わりに例外をスローする場合は、その特定のエラーをキャッチするか、呼び出しを安全チェックでラップする必要があります。

恒久対策:堅牢なコードパターン

Luaスクリプトの改善

引数が存在すると決めつけないでください。メタデータに触れる前に redis.call('EXISTS', KEYS[1]) を使用します。これにより、スクリプトが途中で終了してアプリケーションが不整合な状態になるのを防げます。

-- 安全なメタデータチェックの例
if redis.call("EXISTS", KEYS[1]) == 1 then
    return {
        redis.call("OBJECT", "ENCODING", KEYS[1]),
        redis.call("TTL", KEYS[1])
    }
else
    return nil
end

キー名のサニタイズ

隠れたバイトデータはデバッグの悪夢です。これは、バイナリデータやURLからキーが生成されるときによく発生します。SCANの結果に同じに見えるキーが2回現れたら、隠れた文字が見つかった証拠です。

プロのヒント: 私はPythonのログに末尾の \n が含まれていることに気づかず、存在しないキーを何時間も追いかけたことがあります。生の文字列を検査するには、ToolCraft's Base64 Encoder のようなツールを使用してください。キー名をBase64にエンコードすることで、ターミナルでは表示されない隠れたバイトを特定できます。

Redisプロキシ設定の確認

EnvoyやNutcrackerのようなプロキシは、時として「親切すぎる」ことがあります。キャッシュミスを知らせるために、nil レスポンスをエラーに変換することがあります。この挙動がアプリを壊している場合は、プロキシの設定を確認するか、これらの特定のエラー文字列を適切に処理するようにアプリケーションロジックを更新してください。

検証:修正の確認方法

レースコンディションをシミュレートして、新しいロジックが機能することを確認します:

  • 短命なキーを作成する: SET temp_key "test" EX 2(2秒で期限切れ)。
  • 少し待つ: 3秒待ちます。
  • チェックを実行する: 更新したLuaスクリプトまたはラッパーを実行します。
  • 出力を確認する: ERR no such key でクラッシュするのではなく、制御された (nil) またはカスタムの null が表示されるはずです。

エンコーディングの問題を根絶するには、redis-cli --raw SCAN 0 を実行してください。これにより、厄介な見えない文字がそのまま保持され、キー生成ロジックから特定して削除するのがはるかに容易になります。

Related Error Notes