記事ディレクトリ
序文
最近、「Redis DesignandImplementation」という本を読んでいて本当に良かったです。一気に魅了されました。ありがとうございます。しかし、勉強中に問題が見つかりました。サーバーにRedisバージョン5.0.9がインストールされ、作成者がRedisバージョン3.0を導入しました。データ構造とオブジェクトの章の最初の部分で、いくつかの違いがありました。 redisによって公開されたリスト構造の下部で使用されるデータ構造。本には記録がないので、インターネットで調べて調べて、自分のメモとしてまとめてみました。
差
違いは、redis 3.2より前は、リストキーの基礎となる実装としてziplistとlinkedlistエンコーディングを使用していたことです。その後、quicklistと呼ばれるデータ構造が基礎となる実装として使用されていました。リストキーの基礎となる実装としてziplistとlinkedlistを使用する場合、それらの間に選択基準があります。ziplistを選択する場合:
先来介绍下redis3.2之前的版本的知识点:
- リストオブジェクトに格納されているすべての文字列要素の長さは64バイト未満です。
- リストオブジェクトに格納されている要素の数が512未満です
上記は、基礎となる実装としてziplistを選択するために満たす必要のある条件です。満たさない場合、基礎となる実装としてリンクリストが選択されます。
127.0.0.1:6379> rpush blah "hello" "world" "again"
3
127.0.0.1:6379> object encoding blah
ziplist
127.0.0.1:6379> rpush blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
4
127.0.0.1:6379> object encoding blah
linkedlist
再来介绍下redis3.2之后的版本:
これにはクイックリストのデータ構造が含まれ、本には記録がないので、情報を確認してブログにまとめました。
redisバージョン5.0.9をインストールした場合、上記の命令の実行結果は異なります。
127.0.0.1:6379> rpush blah "hello" "world" "again"
3
127.0.0.1:6379> object encoding blah
quicklist
127.0.0.1:6379> rpush blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
4
127.0.0.1:6379> object encoding blah
quicklist
クイックリストデータ構造の概要
ziplistとlinkedlistは紹介しません。それらは本の中にあります。クイックリストを見てみましょう。
クイックリストの実現は、2つの構造の組み合わせであるziplistとlinkedlistにも依存しています。ziplistをセグメントに格納し、ストレージ用にquicklistNodeノードに分割します。各quicklistNodeはziplistを指し、quicklistNodeは双方向ポインターを介して接続されます。一般的な構造を見てみましょう。
この構造を見ると、いくつか質問があるかもしれません。
- この構造はどういう意味ですか、なぜいくつかのノードがziplistであり、いくつかがquicklistZFであるのですか?
- クイックリストの先頭と末尾にziplistである2つのノードがあり、中央の残りの1つがquicklistZFであるのはなぜですか?
- 各quicklistNodeのziplist内のデータ数に一貫性がないのはなぜですか?
基礎となるデータ構造をクイックリストに最適化するのはなぜですか?
上記の問題を解決する前に、まず主要な問題を解決しましょう。つまり、リストキーの基になるデータ構造をredisがクイックリストに最適化するのはなぜですか。
実際、この理由は2つの側面から考えることができます。
- ziplistの構造、その内部データストレージは連続スペースであり、この場合、大きなメモリスペースが必要です。たとえば、大量のデータを保存したいのですが、メモリ内に要件を満たす連続したストレージスペースがありませんが、不連続な小さなスペースがたくさんあります(合計で要件を満たすことができます)。
- リンクリストの構造について説明します。継続的なデータストレージを必要としないため、上記の欠点を回避できます。ただし、このようにすると、各ノードにメモリが割り当てられ、メモリの断片化が多く発生する可能性があります。
上記の2つの考慮事項に基づいて、redisはバージョン3.2以降、この状況を最適化し、クイックリストのデータ構造が公開されました。クイックリストは実際にはセグメント化されたzipリストです。なぜそう言うのですか?実際、クイックリストストレージデータの基本単位はquicklistNodeであり、各クイックリストノードのコンテンツ領域はziplistデータ構造に格納されます。これは上の図に示されています。
各quicklistNodeのziplist内のデータ数に一貫性がないのはなぜですか?
これでクイックリストに変換されましたが、quicklistNodeのziplistでのコンテンツ処理についてはどうでしょうか。ziplistに保存する必要のあるデータの数はいくつですか?上記2点と同じ
- ziplistのコンテンツの割り当てが少ない場合、つまり、リンクリストの方向に展開されている場合、大量のメモリフラグメントが生成される可能性があります。
- ただし、ziplistのコンテンツがさらに割り当てられると、問題が発生します。つまり、大きな連続したメモリスペースが必要になります。
redisデザイナーはすでにそれについて考えており、その構成ファイルには次のようなパラメーターがあります。list-max-ziplist-size
この構成を参照してください。このパラメーターについて説明しましょう。正の数または負の数を設定できます。
- 正の数をとる場合は、クイックリストノードのziplistの長さがデータ項目の数に応じて制限されることを意味します。たとえば、4に設定すると、各ziplistのデータ項目は最大で5を超えることはできません。
- 負の数をとる場合は、quicklsitノードのziplistの長さが占有バイト数に応じて制限されていることを意味します。現時点では、-1から-5までの5つの値しかとることができず、各値の意味は次のとおりです:
1)、-5:各クイックリストノードのziplistのサイズは64kbを超えることはできません。(1kb == 1024バイト)
2)、-4:各quicklsitノードのziplistのサイズは32kbを超えることはできません。
3)、-3:各quicklsitノードのziplistのサイズは16kbを超えることはできません。
4)、-2:各quicklsitノードのziplistのサイズは8kbを超えることはできません。
5)、-1:各quicklsitノードのziplistのサイズは4kbを超えることはできません。
そのため、ziplistメモリに格納されるデータの数に一貫性がないという問題が発生します。パラメータ値を手動で設定することもできます。
この構造はどういう意味ですか、なぜいくつかのノードがziplistであり、いくつかがquicklistZFであるのですか?
別の構成パラメーターは次のとおりですlist-compress-depth
。実際、リンクリストが非常に長い場合、最も頻繁にアクセスされるデータは両端のデータであり、中央のアクセス頻度は比較的低いため、中央のノードを圧縮してスペースをさらに節約できます。上記のlist-compress-depthは、これを設定するためのものです。
このパラメーターの値の意味は次のとおりです。
- 0:これは特別な値であり、圧縮されないことを意味します。これはredisのデフォルト値です
- 1:クイックリストの両端の1つのノードが圧縮されておらず、中間ノードが圧縮されていることを示します
- 2:クイックリストの両端にある2つのノードが圧縮されておらず、中間ノードが圧縮されていることを示します
- 3:クイックリストの両端に圧縮されていないノードが3つあり、中間ノードが圧縮されていることを示します
- …等々
つまり、問題が発生します。両端のノードはziplistで、中間ノードはquicklistZFです。
上記のredis構成ファイルのデフォルト構成を見てみましょう。
ソースコード構造の簡単な理解
上の図からクイックリストの保存方法について簡単に説明します。下部には、クイックリストとクイックリストノードの2つの構造があります。以下は私が探しているソースコードです、あなたはそれを簡単に見ることができます。
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl;
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
typedef struct quicklistLZF {
unsigned int sz; /* LZF size in bytes*/
char compressed[];
} quicklistLZF;
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; /* total count of all entries in all ziplists */
unsigned int len; /* number of quicklistNodes */
int fill : 16; /* fill factor for individual nodes */
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
クイックリストの主な機能は、ノードの先頭と末尾を指すことです。
総括する
一般に、クイックリストはziplistとlinkedlistの利点を組み合わせたもので、redisのリストキーの基盤となるストレージをさらに最適化します。
最後に書く
もっと本を読んで練習すれば、時間の経過とともに多くの興味深いことが発見されます。