Valkey 9.0 の番号付きデータベースで、より賢いキャッシュを設計する

Valkey 9.0 はクラスターモードに番号付きデータベースを導入しました。小さな変更が、マルチテナント設計、データ分離、アトミックなデータ更新を簡単にします。

Allen Helton Allen Helton
プレゼントを開ける Mo

Momento は最近、CEO の Khawaja Shams が Valkey の DevRel を務める Kyle Davis に、Valkey 9.0 の性能以外のすべてを尋ねる Cache It ポッドキャストを公開しました。

速度のベンチマークを省いてほしいと Khawaja が頼むと、Kyle は「Valkey について、性能の話をせずに話してほしいと頼まれたのは初めてです」と笑いました。その後の対話では、一見単純な機能が、思いもしなかったアーキテクチャのパターンを可能にすることが分かりました。

私は最初、よく理解できませんでした。Kyle は Valkey 9.0 の番号付きデータベースに触れ、クラスタリングと組み合わせることで、その価値が高まると説明しました。用語に戸惑っていましたが、別の説明を聞いて理解できました。

Kyle は番号付きデータベースを「名前空間」と表現しました。このほうがずっと分かりやすく、キーを論理的に分離できるものだと説明しました。同じキー名がデータベース 0 とデータベース 42 に存在しても、まったく異なるデータを持ち、衝突しません。

「Redis 1 にまで遡る機能です」と Kyle は説明しました。「番号付きデータベースという概念があり、持っているキーを、既定では 16 個のデータベースに論理的に分けられます」。

v9 までは、これはスタンドアロンモードだけの機能でした。ほぼすべての本番ワークロードで必要になるクラスターモードを使った瞬間、データベース 0 しか使えなくなりました。この状態が 10 年以上続いていたので、待望の更新です。

Valkey がキーの所属スロットを計算するときに見るのは、キー名だけです。データベース番号は関係ありません。データベース 0 の user:100 と、データベース 5000 の user:100 は、どちらも同じスロットに存在します。

一見、大したことではないように思えます。しかし、その意味を掘り下げると、データベース間でほぼコストをかけずにデータを移せることが分かります。キーの移動はデータ移行ではなく、文字どおりポインターの変更です。300 万要素を含むソート済みセットでも、ネットワーク転送なしにデータベース間で瞬時に移動できます。

解決できる課題

私はすぐに、これで解決できる現実の課題を考え始めました。そして、非常に魅力的な用途をいくつか見つけました。

1. キーのプレフィックスが不要に

「複数のデータベース」と聞いてまず思い浮かぶ用途は、マルチテナントです。マルチテナントの SaaS アプリでは、私と同じように、次のようなキーを作っているのではないでしょうか。

📄

1

2

3

cache.set("tenant_12345:user:100", user_data)
cache.set("tenant_12345:session:abc", session_data)
cache.set("tenant_12345:cart:xyz", cart_data)

取得、設定、削除するすべてのキーに、テナント ID のプレフィックスが付きます。これは一般に、顧客データを論理的に分離するための安全な方法です。しかし、1 億個のキーに 15 バイトのプレフィックスを付けると、同じ文字列を繰り返し保存するだけで 1.5GB の RAM を使います。非常に無駄の多いパターンです。

今後は、一連のキーをテナント専用のデータベースに保存できます。コードが簡潔になるだけでなく、多くのメモリが解放され、さらに多くのキーに使えるようになります。

📄

1

2

3

4

cache.select(12345)  // Tenant 12345's namespace
cache.set("user:100", user_data)
cache.set("session:abc", session_data)
cache.set("cart:xyz", cart_data)

つまり、ハードウェアを変更せずに、扱えるワーキングセットの規模を実質的に広げられます。同じ分離性を保ち、プレフィックスのオーバーヘッドはゼロです。Valkey 8.0 がキーあたり 8 バイトの削減で注目されたことを思い出してください。ここでは 10~20 バイトをなくせるのです。

2. ブラックフライデー規模の価格切り替え

Kyle が説明した用途を聞き、私はこれまで見てきたあらゆる EC システムを思い浮かべました。

「あるデータの集合から別の集合へ、瞬時に、アトミックに切り替えたいとします。100 万要素があり、午前 12:01 になったら別の 100 万要素にしたい、という場合です」。

まさにホリデーセールの価格設定です。企業は全商品と価格のカタログをキャッシュクラスターから提供します。感謝祭が終わる深夜に、すべてを瞬時にセール価格へ切り替える必要があります。

通常はバッチジョブで価格を一括更新したり、機能フラグでデータを更新したりします。しかし、同じように誤りが起きやすい処理では、顧客に旧価格と新価格が混在して見えることがあります。

複数のデータベースがあれば、別のデータベースにカタログを構築し、一つのコマンドで瞬時に移せます。

📄

cache.select(2)  // holiday pricing database
cache.move("product:prices", 1) # moved to production database

データのコピーもネットワークのオーバーヘッドもありません。設定変更だけで、価格体系全体が切り替わります。実際に見せてもらうまでは、不可能に思えるような機能です。

3. 並列のテスト環境

これは私自身にも身近な課題です。CI/CD でテストを実行していると、分離したデータを必要とする複数のテストジョブを並列に動かし、互いに干渉した経験があるかもしれません。

もちろん、テストを並列に実行せず、ジョブごとにテストデータをリセットすれば回避できます。しかし、今はその時間をかけられません。複数のデータベースがあれば、この問題は簡単に解決できます。

🐍

test_db = int(os.getenv("CI_JOB_ID")) % 10000
cache = Valkey(host='valkey-cluster')
cache.select(test_db)
cache.set("user:test@example.com", user_json)
assert cache.get("user:test@example.com") == user_json

cache.flushdb()

拡張性もあります。ただし、少し補足が必要です。ポッドキャストでは、挙動を確かめるために 1,000 万個のデータベースを持つクラスターを動かした話が出ました。結果は問題なしでした。Valkey はデータベースを遅延割り当てするため、実際にデータを入れたときだけリソースを使います。そのため、数千の同時テスト実行に固有のデータベース番号を割り当てても、データを書き込むまではコストがかかりません。

インターネット越しに開発者とハイタッチしたくなるような設計です。🙌

今後も機能が増えるかもしれません

Kyle は、番号付きデータベースを Valkey の主要な機能としてさらに使いやすくする拡張にも触れました。ただし、実現を約束したわけではありません。

名前付きデータベース:SELECT 42 の代わりに、SELECT "production" や SELECT "tenant_acme_corp" が使えるところを想像してください。開発者体験が大きく向上するだけでなく、マルチテナント環境でも、テナント ID と番号の Key-Value 対応表を管理する必要がなくなります。

データベースごとのメモリ上限:テストデータベースが RAM をすべて消費するのを防ぎます。テスト環境を分離し、全リソースを占有させないようにできます。マルチテナントの本番環境で、一部の利用者が他へ影響する問題にも対処できます。

データベースごとのレート制限:名前空間の単位で、マルチテナントのリソースを実際に分離できます。

クラスターモードの番号付きデータベースにより、予測可能で軽量な分離がキャッシュ層そのものに組み込まれます。企業のワークロードにとって大きな変化です。マルチテナントアプリは簡単になり、A/B テストや CI 環境は安全になり、展開や移行のリスクも減ります。

Kyle は、次のように端的にまとめました。

データベースをまったく意識せずに使う代わりに SELECT 4 とするだけで、すぐにメモリを節約したり、まったく新しい用途を実現したりできます。

小さく見えて強力なこの機能は、今後の分散キャッシュの設計方法を変えるかもしれません。

さらに詳しく知るには、valkey.io をご覧になるか、コミュニティの Slack に参加してください。また、ポッドキャスト全編もぜひご視聴ください。ここでは触れられなかったハッシュフィールドの有効期限やガバナンスモデルについても、多くの有益な話があります。

あわせて読みたい