web-dev-qa-db-ja.com

Redisに使用される基礎となるデータ構造は何ですか?

私は決定的なリストの2つの質問に答えようとしています。

  1. Redisに使用される基礎となるデータ構造は何ですか?
  2. そして、各タイプの主な利点/欠点/ユースケースは何ですか?

だから、私はRedisリストが実際にリンクされたリストで実装されていることを読んだ。しかし、他のタイプの場合、情報を掘り下げることはできません。また、誰かがこの質問に出くわし、さまざまなデータ構造を変更またはアクセスすることの長所と短所の概要を把握していない場合、最高のタイミング特定のタイプを使用して参照することもできます。

具体的には、文字列、リスト、セット、zset、およびハッシュのすべてのタイプの概要を探しています。

ああ、私はこれらの記事を特にこれまで見てきました。

297
Homer6

私はあなたの質問に答えようとしますが、最初は奇妙に見えるかもしれない何かから始めます:Redisの内部に興味がないなら、あなたは気にしないでくださいデータ型が内部的に実装される方法について。これは単純な理由です:Redisのすべての操作について、ドキュメントに時間の複雑さがあります。操作のセットと時間の複雑さがある場合、必要な他の唯一のものはメモリ使用量に関する手がかりです(そしてデータに応じて変化する可能性のある多くの最適化を行います。後者の数値を取得する最良の方法は、いくつかの些細な現実世界のテストを行うことです)。

しかし、あなたが尋ねたので、ここにすべてのRedisデータ型の基礎となる実装があります。

  • StringsはC動的文字列ライブラリを使用して実装されているため、追加操作での割り当てに(漸近的に言えば)支払いをしません。このようにして、たとえば、2次の動作をする代わりに、O(N)を追加します。
  • リストはリンクリストで実装されます。
  • SetsおよびHashesはハッシュテーブルで実装されます。
  • ソートされたセットリストのスキップ (固有のバランスの取れたタイプのツリー)で実装されます。

しかし、リスト、セット、およびソートされたセットのアイテム数と最大値のサイズが小さい場合、異なる、よりコンパクトなエンコードが使用されます。このエンコードはタイプによって異なりますが、多くの場合、すべての操作に対してO(N)スキャンを強制するコンパクトなデータの塊であるという特徴があります。この形式は小さなオブジェクトにのみ使用するため、これは問題ではありません。小さなO(N) blobのスキャンはcache obliviousであるため、実際には非常に高速であり、要素が多すぎるとエンコードは自動的に切り替えられますネイティブエンコーディング(リンクリスト、ハッシュなど)。

しかし、あなたの質問は実際には内部についてだけではなく、あなたのポイントは何を達成するためにどのタイプを使用するかでしたか?.

ひも

これは、すべてのタイプの基本タイプです。 Listは文字列のリスト、Setは文字列のセットなどであるため、4つの型の1つですが、複合型の基本型でもあります。

Redis文字列は、HTMLページを保存するすべての明白なシナリオだけでなく、既にエンコードされたデータの変換を避けたい場合にも適しています。たとえば、JSONまたはMessagePackがある場合、オブジェクトを文字列として保存するだけです。 Redis 2.6では、Luaスクリプトを使用してこの種のオブジェクトサーバー側を操作することさえできます。

文字列のもう1つの興味深い使用法はビットマップであり、一般的にはバイトのランダムアクセス配列です。これは、Redisがランダムなバイト範囲または単一ビットにアクセスするコマンドをエクスポートするためです。たとえば、 この良いブログ記事:Redisを使用したFast Easyリアルタイムメトリックス をチェックしてください。

リスト

リストは、リストの極端な部分(尾の近く、頭の近く)のみに触れる可能性がある場合に適しています。ランダムアクセスは遅いためO(N)であるため、リストはページ分割にはあまり適していません。リストの適切な使用法は、単純なキューとスタック、または同じソースと宛先でRPOPLPUSHを使用してループ内のアイテムを処理し、アイテムのリングを「回転」させることです。

リストは、通常だけにアクセスするN個のアイテムの上限付きコレクションを作成する場合、またはNが小さい場合にも適しています。

セット

セットは順不同のデータコレクションであるため、アイテムのコレクションがあるたびに有効であり、コレクションの存在またはサイズを非常に高速に確認することが非常に重要です。セットに関するもう1つの素晴らしい点は、ランダム要素のピークまたはポップのサポートです(SRANDMEMBERおよびSPOPコマンド)。

セットは、リレーションを表すのにも適しています。たとえば、「ユーザーXの友達は何ですか?」などなど。しかし、この種のものに適した他のデータ構造は、後で説明するようにソートされたセットです。

セットは交差点や共用体などの複雑な操作をサポートするため、データがあり、そのデータに対して変換を実行して出力を取得する場合に、「計算」方法でRedisを使用するのに適したデータ構造です。

小さなセットは非常に効率的な方法でエンコードされます。

ハッシュ

ハッシュは、フィールドと値で構成されるオブジェクトを表すのに最適なデータ構造です。ハッシュのフィールドは、HINCRBYを使用してアトミックにインクリメントすることもできます。ユーザー、ブログの投稿、または他の種類のitemなどのオブジェクトがある場合、JSONのような独自のエンコーディングを使用したくない場合は、ハッシュを使用する方法がありますまたは類似。

ただし、小さなハッシュはRedisによって非常に効率的にエンコードされ、Redisに個々のフィールドを非常に高速にアトミックにGET、SETまたはインクリメントするように要求できることに注意してください。

ハッシュは、参照を使用して、リンクされたデータ構造を表すためにも使用できます。たとえば、lamernews.comのコメントの実装を確認してください。

ソートセット

ソートされたセットは、リスト以外の、順序付けられた要素を維持するための唯一のデータ構造です。ソートされたセットを使用すると、多くのクールなことができます。たとえば、あらゆる種類のTop SomethingリストをWebアプリケーションに含めることができます。スコアによるトップユーザー、ページビューによるトップポスト、トップは何でも、1つのRedisインスタンスは1秒間に大量の挿入操作とトップエレメント取得操作をサポートします。

通常のセットと同様に、ソートされたセットは関係を記述するために使用できますが、アイテムのリストをページ分割して順序を記憶することもできます。たとえば、ソートされたセットを持つユーザーXの友人を覚えている場合、受け入れられた友情の順に簡単に思い出すことができます。

並べ替えられたセットは、優先キューに適しています。

ソートされたセットは、リストの中央からの範囲の挿入、削除、または取得が常に高速である、より強力なリストのようなものです。ただし、より多くのメモリを使用し、O(log(N))データ構造です。

結論

この投稿で何らかの情報を提供したいと思いますが、lamernewsのソースコードを http://github.com/antirez/lamernews からダウンロードして、その仕組みを理解する方がはるかに良いでしょう。 Redisの多くのデータ構造がLamer News内で使用されており、特定のタスクを解決するために何を使用するかについて多くの手がかりがあります。

文法のタイプミスで申し訳ありませんが、ここは深夜なので、投稿を確認するには疲れすぎています;)

593
antirez

Redisは、値を指すキーを保存します。キーは、妥当なサイズまでの任意のバイナリ値にすることができます(読みやすさとデバッグの目的で、短いASCII文字列を使用することをお勧めします)。値は、5つのネイティブRedisデータ型の1つです。

1.strings — 512 MBまでの一連のバイナリセーフバイト

2.hashes —キーと値のペアのコレクション

3.lists —文字列の挿入順のコレクション

4.sets —順序付けのない一意の文字列のコレクション

5.sorted sets —ユーザー定義のスコア順に並べられた一意の文字列のコレクション

文字列

Redis文字列はバイトのシーケンスです。

Redisの文字列はバイナリセーフです(つまり、特殊な終端文字によって決定されない既知の長さを持っていることを意味します)。したがって、1つの文字列に最大512メガバイトを格納できます。

文字列は、標準的な「キー値ストア」の概念です。値を指すキーがあり、キーと値の両方がテキストまたはバイナリ文字列です。

文字列で可能なすべての操作については、 http://redis.io/commands/#string を参照してください

ハッシュ

Redisハッシュは、キーと値のペアのコレクションです。

Redisハッシュは多くのキーと値のペアを保持します。各キーと値は文字列です。 Redisハッシュは、複素数値を直接サポートしていません(つまり、ハッシュフィールドにリストまたはセットまたは別のハッシュの値を持たせることはできません)が、ハッシュフィールドを使用して他のトップレベルの複素数値を指すことができます。ハッシュフィールド値に対して実行できる特別な操作は、数値コンテンツのアトミックなインクリメント/デクリメントのみです。

Redisハッシュは、2つの方法で考えることができます:直接的なオブジェクト表現として、および多くの小さな値をコンパクトに格納する方法として。

直接オブジェクト表現は理解しやすいです。オブジェクトには、名前(ハッシュのキー)と値を持つ内部キーのコレクションがあります。例については、以下の例を参照してください。

ハッシュを使用して多くの小さな値を保存することは、賢明なRedisの大量データ保存技術です。ハッシュのフィールド数が少ない(〜100)場合、Redisはハッシュ全体のストレージとアクセス効率を最適化します。 Redisの小さなハッシュストレージの最適化は興味深い動作を引き起こします。10,000個のトップレベルキーが文字列値を指すのではなく、100個のハッシュにそれぞれ100個の内部キーと値が含まれる方が効率的です。この方法でRedisハッシュを使用してデータストレージを最適化するには、データが終了する場所を追跡するための追加のプログラミングオーバーヘッドが必要ですが、データストレージが主に文字列ベースの場合、この奇妙なトリックを使用して多くのメモリオーバーヘッドを節約できます。

ハッシュで可能なすべての操作については、 hash docs を参照してください

リスト

Redisリストはリンクリストのように機能します。

リストの先頭または末尾からリストを挿入、削除、およびトラバースできます。

挿入された順序で値を維持する必要がある場合は、リストを使用します。 (Redisには、必要に応じて任意のリスト位置に挿入するオプションがありますが、開始位置から遠くに挿入すると挿入パフォーマンスが低下します。)

Redisリストは、生産者/消費者キューとしてよく使用されます。リストにアイテムを挿入し、リストからアイテムをポップします。消費者が要素のないリストからポップしようとするとどうなりますか? Redisに要素が表示されるのを待って、要素が追加されたらすぐにそれを返すように依頼できます。これにより、Redisはリアルタイムのメッセージキュー/イベント/ジョブ/タスク/通知システムに変わります。

リストの両端から要素をアトミックに削除して、リストをスタックまたはキューとして扱うことができます。

また、挿入のたびにリストを特定のサイズにトリミングすることにより、固定長リスト(キャップ​​付きコレクション)を維持することもできます。

リストで可能なすべての操作については、 lists docs を参照してください

セット

Redisセットは、まあ、セットです。

Redisセットには、各ストリングがセットごとに1つだけ存在する、一意の順序付けられていないRedisストリングが含まれます。同じ要素をセットに10回追加すると、1回だけ表示されます。セットは、重複する要素が蓄積してスペースを浪費することを心配することなく、少なくとも1回は何かが遅延して存在することを保証するのに最適です。同じ文字列は、既に存在するかどうかを確認することなく、何度でも追加できます。

セットは、セット内のメンバーのメンバーシップチェック、挿入、および削除が高速です。

ご想像のとおり、セットには効率的なセット操作があります。複数のセットの結合、交差、および差を一度に取得できます。結果を呼び出し元に返すか、結果を後で使用するために新しいセットに保存できます。

セットには、メンバーシップチェック(リストとは異なり)への一定時間のアクセスがあり、Redisには便利なランダムメンバーの削除と返却(「セットからのランダム要素のポップ」)またはランダムメンバーの置き換えなしの返却(「ランダムな30人のユニークユーザーを30個ください」 ")または交換(「7枚のカードをください。ただし、選択するたびにカードを戻して、再度サンプリングできるようにします」)。

セットで可能なすべての操作については、 sets docs を参照してください。

ソートされたセット

Redisソートセットは、ユーザー定義の順序を持​​つセットです。

簡単にするために、ソートされたセットは一意の要素を持つバイナリツリーと考えることができます。 (Redisソートセットは、実際には リストのスキップ です。)要素のソート順は、各要素のスコアによって定義されます。

ソートされたセットは引き続きセットです。要素はセット内で1回のみ表示できます。一意性を目的とした要素は、文字列の内容によって定義されます。並べ替えスコア3の要素「Apple」を挿入し、並べ替えスコア500の要素「Apple」を挿入すると、並べ替えられたセット内の並べ替えスコア500の1つの要素「Apple」になります。セットは、(スコア、データ)のペアではなく、データに基づいてのみ一意です。

データモデルが一意性の要素のスコアではなく、文字列の内容に依存していることを確認してください。スコアの繰り返し(またはゼロ)も許可されますが、最後にもう一度、セット要素はソートされたセットごとに1つしか存在できません。たとえば、スコアをログインのエポック、値をユーザーIDにして、すべてのユーザーログインの履歴をソート済みセットとして保存しようとすると、すべてのユーザーの最後のログインエポックのみが保存されます。セットは、ユーザーベース*ログインの目的のサイズではなく、ユーザーベースのサイズに拡大します。

要素は、スコアとともにセットに追加されます。任意の要素のスコアをいつでも更新できます。新しいスコアで要素を再度追加するだけです。スコアは浮動小数点の倍精度で表されるため、必要に応じて高精度のタイムスタンプの粒度を指定できます。複数の要素が同じスコアを持つ場合があります。

いくつかの異なる方法で要素を取得できます。すべてがソートされているため、最低スコアから開始する要素を要求できます。最高のスコアから始まる要素を要求できます(「逆」)。自然順または逆順のソートスコアで要素を要求できます。

ソートされたセットで可能なすべての操作については、 sorted sets docs。 を参照してください。

2
shrikant