Redis文字列型は注意して使用してください。

文字列を注意して使用しますか?

開く前に、グループ比較を行います。

1. 这是执行 flushdb 后的干净的 Redis 内存使用信息。

127.0.0.1:6379> info memory
# Memory
used_memory:502272
used_memory_human:490.50K
used_memory_rss:7901184
used_memory_peak:119628904
used_memory_peak_human:114.09M
used_memory_lua:33792
mem_fragmentation_ratio:15.73
mem_allocator:jemalloc-3.6.0

2. 执行100000次(一百万)循环执行 set 操作。

// 代码仅作参考
$redisHandle = Redis::connection('intranet_log_redis');
for ($i= 0; $i < 1000000; $i++) {
    
    
    $redisHandle->set($i, 10000000);
}

// 执行步骤2后的内存信息。
127.0.0.1:6379> info memory
# Memory
used_memory:72891064
used_memory_human:69.51M
used_memory_rss:80601088
used_memory_peak:134217744
used_memory_peak_human:128.00M
used_memory_lua:33792
mem_fragmentation_ratio:1.11
mem_allocator:jemalloc-3.6.0

// 由上信息可知除去本身redis占用的内存,一百万个键值对使用了69M的内存。

3. 再次执行 flushdb 后,改为hash类型存储,再次执行1000000(一百万)次循环。

// 代码仅供参考
$redisHandle = Redis::connection('intranet_log_redis');
for ($i= 0; $i < 1000000; $i++) {
    
    
    $redisHandle->hset('testKey', $i, 10000000);
}

// 执行步骤3后查看内存信息
127.0.0.1:6379> info memory
# Memory
used_memory:72883104
used_memory_human:3.43M
used_memory_rss:82509824
used_memory_peak:134217744
used_memory_peak_human:128.00M
used_memory_lua:33792
mem_fragmentation_ratio:1.13
mem_allocator:jemalloc-3.6.0

当当当! 使用了hash存储,内存使用减少了 20 倍!!!这是为什么尼???
文字列型のストレージ構造

同じデータを同じ量のデータで保存するときに、文字列がハッシュの20倍になるのはなぜですか?この質問で、文字列の特定の実装を学びます。

上記の例では、値の部分は1000000であり、8バイトのLong型に格納できます。キーは0〜1000000で、Longに保存することもできます。理論的には、ストレージには数メガバイトのメモリしか必要ありませんが、69メガバイトが使用されるのはなぜですか?

これは、文字列のエンコードと構造化を目的として設計されています。たとえば、64ビットシステムは整数を保存します。Redisは、ストレージに8バイトのLong型を使用します。これは、intエンコード方式です。

SDS(​​Simple Dynamic String)単純動的文字列構造ストレージを使用すると、文字列は同じではありません。

構造 サイズ 説明
len 4B 4バイト、bufの使用された長さを示します
alloc 4B 4バイト、bufの実際に割り当てられた長さを表し、通常はlenより大きい
buf 配列、実際のデータを保持します。 配列の後ろに「\ 0」を自動的に接続して、データの終わりをマークします。1バイトを占める

上記の表から、SDSにはlenとallocの追加のストレージオーバーヘッドがあることがわかります。これは、Mogodbに少し似ています。もちろん、文字列型の場合、SDSのオーバーヘッドに加えて、RedisObject構造もあります。その機能は、データを記録するために使用されるメタデータが同時にこれらのデータを指すことです。

RedisObjectには、8バイトのメタデータと8バイトのポインター(実際のデータの場所)が含まれています。もちろん、メモリスペースを節約するために、RedisはLong型整数とSDSのメモリレイアウト用に特別な設計も行いました。

例:Long型整数が格納されている場合、RedisObjectのポインターは整数データに直接割り当てられるため、整数を指す追加のポインターは必要ありません。これにより、ポインターのスペースオーバーヘッドが節約されます。

文字列データが格納され、文字列が44バイト以下の場合、RedisObjectのメタデータ、ポインタ、およびSDSは連続したメモリ領域であるため、メモリの断片化を回避できます。このレイアウト方法は、embstrエンコーディングとも呼ばれます。
文字列が44バイトを超えると、SDS内のデータ量が増加し始め、RedisはSDSとRedisObjectを一緒にレイアウトしなくなりますが、独立したスペースをSDSに割り当て、ポインターを使用してSDS構造を指します。このレイアウト方法は、rawエンコーディングモードと呼ばれます。

上記のリストは、文字列構造とRedisObjectによってもたらされる追加のストレージオーバーヘッドですが、これが唯一のポイントではありません。Redisは、グローバルハッシュテーブルを使用してすべてのキーと値のペアを格納します。ハッシュテーブルの各アイテムは、キーと値のペアを指すdictEntry構造です。dictEntry構造体には3つの8バイトポインタがあり、それぞれキー、値、次のdictEntryを指します。次の図に示すように、3つのポインタは合計24バイトです。
ここに画像の説明を挿入
上記の3つのポインタは24バイトを共有しますが、実際にはRedisです。 Redisが使用するメモリ割り当てjemallocに関連する32バイトを彼に割り当てます。jemallocがメモリを割り当てると、Nを要求したバイト数に応じて、割り当てられたスペースとしてNよりも大きいがNに最も近い2の累乗が検出されます。これにより、頻繁な割り当ての数を減らすことができます。したがって、32だけが24に近く、24より大きくなります。

したがって、上記の2つの理由は、追加のメモリオーバーヘッドをもたらす場所が多数あるため、69Mを使用して100万のデータを格納することです。では、ハッシュを使用するときになぜこれほど小さいのでしょうか。

ハッシュストレージ

Redisには、ziplistと呼ばれる低レベルのデータ構造があります。これは非常にメモリを節約する構造です。圧縮リストのヘッダーには、zlbytes、zltail、zllenの3つのフィールドがあり、それぞれリストの長さ、リストの終わりのオフセット、およびリスト内のエントリーの数を表します。圧縮リストの最後には、リストの終わりを示すzlendもあります。図に示すように、
圧縮リスト
圧縮リストがメモリを節約できる理由は、一連の連続したエントリを使用してデータを保存するためです。接続に追加のポインターが使用されないため、スペースが節約されます。各エントリのメタデータには、次の部分が含まれています。

構造 説明
prev_len 、前のエントリの長さを示します。prev_lenには2つの値があります:1バイトまたは5バイト。値が1バイトの場合、前のエントリの長さが254バイト未満であることを意味します。1バイトの値で表すことができる値の範囲は0〜255ですが、圧縮リストのzlendのデフォルト値は255です。したがって、255は、デフォルトで圧縮リスト全体の終わりを示すために使用されます。長さを示す場所は使用できません。この値は255です。したがって、前のエントリの長さが254バイト未満の場合、prev_lenの値は1バイトです。それ以外の場合、値は5バイトです。
len 自己長4バイト
エンコーディング エンコード方式1バイト
コンテンツ 実際のデータを保存する

もちろん、ハッシュタイプの2つの基本的な実装構造は、圧縮リストとハッシュテーブルです。ハッシュテーブルまたは圧縮リストの適切な使用は、次の2つのパラメータセットによって制御されます。
hash-max-ziplist-entries:圧縮リストに保存するときのハッシュセット内の要素の最大数を示します。デフォルトは512です。
hash-max-ziplist-value:圧縮リストに保存するときのハッシュセット内の単一要素の最大長を示します。デフォルトは60です。

ハッシュセットに書き込まれる要素の数がhash-max-ziplist-entriesを超える場合、または書き込まれる単一の要素のサイズがhash-max-ziplist-valueを超える場合、Redisはハッシュタイプの実装構造を圧縮から自動的に変更しますlistハッシュテーブルに変換します。圧縮リストがハッシュテーブルに変換されると、ハッシュタイプは常にハッシュテーブルに格納され、圧縮リストに変換されることはありません。メモリスペースを節約するという点では、ハッシュテーブルは圧縮リストほど効率的ではありません。

記事で実験しているときに、hash-max-ziplist-entriesを100万に調整しました。したがって、キーと値のペアを多数格納する場合は注意が必要であり、それについて詳しく考える必要があります。たとえば、ハッシュタイプの2次エンコード方式を使用できます。

おすすめ

転載: blog.csdn.net/m0_51504545/article/details/109372792