現在のKey-Valueストレージがアイテムの「有効期限」を実装する方法について考えていました。現在、私はそのために2つのバリエーションを持っています。
彼らは「期限切れが最も早い」ことを可能にするために追加のデータ構造を保持します。私はそれがこのようなものでできることがわかります:
storage_data = dict(key -> [value, expire_timestamp])
expire_tree = SomeBinaryLikeTree(expire_timestamp -> [keys])
キャッシュ内の期限切れのエントリを削除する問題は、参照カウントの全体的な複雑さを差し引いた ガベージコレクション とほぼ同じです。
Nasza-Klasaの人々は、MemcacheのO(1)アルゴリズムを次のように提案しています。
何らかの理由で、期限切れのエントリを解放することはO(1)では実行できない、あるいはOmega(N)操作が必要であるとさえ信じているようです。ヒープまたは他の優先キューデータ構造を使用すると、明らかにO(log N)が得られますが、以下のパッチはO(1)を目的としています。これは、1秒ごとに1つのバケットを使用し、有効期限を確認して各エントリを適切なバケットに配置することで実現されます。その後、毎秒、次のバケットから要素を解放します。これは明らかにO(1)償却時間ですが、同じ時点で期限切れになる要素がたくさんある場合があるため、このパッチでは操作の数に一定の制限がありますガベージコレクションをよりスムーズに実行するために、1つのリクエストごとに実行してもかまいません。
コードが添付された全体の提案 を参照してください。
Key-Valueストレージが大きすぎて、すべてのk-vペアを繰り返し処理して、期限切れになる可能性があるものを見つけることができないと思います。また、各読み取りアクセスで有効期限のタイムスタンプが更新されるため、しばらくアクセスされなかったアイテムのみが期限切れになると想定しています。
課題は、有効期限が切れる可能性のあるすべてのレコードを効率的に見つけることです(クリーンアップが必要なときはいつでも)。また、すべての読み取りアクセスで有効期限のタイムスタンプを効率的に更新します(したがって、有効期限に使用される構造でキーを見つける必要があります)。
私の提案:expiry_timestampsをバケットにグループ化します。たとえば、アイテムが8時間存続する場合は、1時間に1つのバケットを作成します。それらのバケットはリンクされたリストに保持されます。期限切れになると、最初のバケットが空になり、リストが減ります。バケットの数は、寿命/クリーンアップ間隔です。各バケットには、期限切れにする必要のあるすべてのキーのhashSetが含まれています。ハッシュセット内のすべてのキーの反復は十分に効率的です。
読み取りアクセス中に、プログラムは、キーが現在どのバケットにあり、どのバケットに現在属しているかをチェックします。ほとんどの場合、同じバケットであるため、これ以上のアクションは必要ありません。それ以外の場合は、古いバケットからキーを削除し(ハッシュセットから削除すると効率的です)、新しいバケットに挿入します。
+--------------+ +--------------+ +--------------+
-->+ Expiry 08:00 +-->+ Expiry 09:00 +-->+ Expiry 10:00 +
| KeySet | | KeySet | | KeySet |
+--------------+ +--------------+ +--------------+