Redisの内郚ラむン

Redisの単玔な文字列 `strings`がRAMで56バむトを占める理由を知っおいるなら、この蚘事には興味がないず思いたす。 Redisにはどのような文字列があるのか​​、そしおこのデヌタベヌスを䜿甚する開発者がどのように動䜜し、動䜜するのかを理解するこずが重芁である理由を他の人に䌝えようずしたす。 この知識は、アプリケヌションの実際のメモリ消費量を蚈算しようずしおいる堎合、たたは負荷の高い統蚈たたはデヌタアカりンティングシステムを構築する蚈画を立おおいる堎合に特に重芁です。 たたは、よくあるこずですが、redisむンスタンスが突然倧量のメモリを消費し始めた理由を緊急に理解しようずしおいたす。



私が話すのは、Redisでの文字列の保存方法、文字列の保存に䜿甚される内郚構造、Redisが内郚で䜿甚する最適化の皮類です。 倧きな構造䜓を効果的に栌玍する方法ず、それらに基づいお構築された文字列たたは構造䜓を䜿甚する䟡倀がない状況。 ラむンはRedisの重芁な構造であり、HSET / ZSET / LISTはそれらのベヌスに基づいお構築され、内郚構造を衚す小さなオヌバヌヘッドが远加されたす。 この蚘事が必芁な理由-redisタグのstackoverflowを1幎以䞊読んで積極的に察応しおいたす。 この間、開発者がRAMを操䜜するRedisの機胜ず高速化のために支払う必芁があるものを理解しおいないずいう事実に関連しお、垞にさたざたな質問が絶え間なく流れおいたす。



実際に䜿甚されるメモリ量の質問に察する答えは、オペレヌティングシステム、コンパむラ、プロセスのタむプ、䜿甚されるアロケヌタredisでは、デフォルトではjemallocによっお異なりたす。 centos 7を実行しおいる64ビットサヌバヌでアセンブルされたredis 3.0.5のすべおの蚈算を行いたす。

ここでは、C / C ++を曞いおいない人、たたはすべおが䜎レベルでどのように機胜するかに぀いおあたり詳しくない人には、ここで少し間奏が必芁だず思われたす。 蚈算を理解しやすくするために、いく぀かの抂念を非垞に単玔化したしょう。 C / C ++プログラムで構造䜓を宣蚀し、その䞭に笊号なし敎数フィヌルド4バむトの笊号付き敎数なしがある堎合、コンパむラヌはそのサむズを実際のRAMで8バむトに慎重に調敎したすx64アヌキテクチャヌの堎合。 この蚘事では、メモリアロケヌタに぀いお定期的に説明したす。これは、メモリを「スマヌトに」割り圓おる皮類です。 たずえば、jemallocは、割り圓おられたフラグメントのアラむメントに䟝存しお、メモリの新しいブロックの怜玢速床を最適化しようずしたす。 jemallosのメモリ割り圓おずアラむンメント戊略に぀いおは十分に説明されおいたすが、遞択したフラグメントのサむズは最も近い次数2に䞞められるずいう単玔化を䜿甚する必芁があるず思いたす。あなたがもう少しはっきりするこずを願っおいたす。 これらは、むンタヌプリタヌ蚀語では、論理的に心配するべきではないこずですが、ここではそれらに泚意を向けるようお願いしたす。



Salvatore Sanfilippo別名antirezによる著者によるラむンの抂念ず実装は、SDSSimple Dynamic String、 github.com / antirez / sds ず呌ばれる倧根プロゞェクトの1぀にありたす。

+--------+-------------------------------+-----------+ | Header | Binary safe C alike string... | Null term | +--------+-------------------------------+-----------+ | `-> Pointer returned to the user.
      
      







これは単玔な `c`構造であり、その芋出しには、珟圚割り圓おられおいるメモリの珟圚のサむズず空き領域、行自䜓、および倧根自䜓が远加する必須の終了れロが栌玍されたす。 sds行では、ヘッダヌのコスト、ヘッダヌのサむズを倉曎する戊略、およびメモリを割り圓おる際に構造を敎列するペナルティに最も関心がありたす。



2015幎7月4日、 ラむンの最適化で長い話が終わりたした。これは倧根3.1に分類されるはずです。 この最適化により、ラむンヘッダヌのメモリが倧幅に節玄されたす合成テストで16から200。 倧根の最倧行長の512MBの制限を削陀したす。 これはすべお、文字列の長さを倉曎するずきにヘッダヌの長さが動的に倉曎されるために可胜になりたす。 したがっお、ヘッダヌは、最倧256バむトの文字列では3バむト、65 kb未満の文字列では5バむト、最倧512 mbの文字列では9バむト、サむズがuint64_t64ビットに適合する文字列では17バむトのみを占有したす笊号なし敎数。 ずころで、この倉曎により、実際のファヌムは玄19.3のメモリ〜42 GBを節玄したす。 ただし、最新のヘッダヌ3.0.xでは、8バむト+ 1バむトから最埌のれロたですべおがシンプルです。 文字列「文字列」に必芁なメモリ量を掚定しおみたしょう16ヘッダヌ+ 7文字列の長さ+ 1末尟のれロ= 24バむトコンパむラヌは2぀の笊号なしintを調敎するため、ヘッダヌごずに16バむト。 その際、jemallocは32バむトを割り圓おたす。 ずりあえずこれを省略したしょう理由が埌で明らかになるこずを望みたす。



文字列のサむズを倉曎するずどうなりたすか 行のサむズを倧きくし、すでに十分なメモリが割り圓おられおいない堎合、倧根は定数SDS_MAX_PREALLOCsds.hで定矩され、1,048,576バむトで新しい長さをチェックしたす。 新しい長さがこの倀より小さい堎合、メモリは芁求された量の2倍に割り圓おられたす。 文字列の長さがすでにSDS_MAX_PREALLOCを超えおいる堎合、この定数の倀は新しい芁求された長さに远加されたす。 この機胜は、「ビットマップを䜿甚するずメモリが消える」ずいう話で重芁になりたす。 ずころで、ビットマップにメモリを割り圓おる堎合、setbitコマンドの実装の特性により、芁求された量の2倍が垞に割り圓おられたすbitops.cのsetbitCommandを参照。



これで、ラむンがRAMで32バむトを占有するず蚀うこずができたすアラむンメントを考慮しお。 hashedin.com  redisメモリ最適化ガむド から連䞭のヒントを読んだ人は、100バむト未満の文字列を䜿甚しないこずを匷くお勧めするこずを思い出すかもしれたせん。 「set foo bar」コマンドを䜿甚する堎合など、短い文字列を保存するには、〜96バむトを費やしたす。そのうちの90バむトはオヌバヌヘッドです64ビットマシン䞊。 圌らはcですか 次に考えおみたしょう。



倧根のすべおの倀はredisObject型の構造䜓に保存されたす。 これにより、倧根は倀のタむプ、その内郚衚珟倧根ではこれを゚ンコヌドず呌びたす、LRUのデヌタ、倀の倀を参照するオブゞェクトの数、および倀自䜓を知るこずができたす。

 +------+----------+-----+----------+-------------------------+ | Type | Encoding | LRU | RefCount | Pointer to data (ptr*) | +------+----------+-----+----------+-------------------------+
      
      







少し埌に、コンパむラのアラむメントずjemalloc機胜を考慮しお、ラむンのサむズを蚈算したす。 文字列に関しおは、文字列を保存するためにどの゚ンコヌディングが䜿甚されおいるかを知るこずが非垞に重芁です。 珟圚、倧根は3぀の異なるストレヌゞ戊略を䜿甚しおいたす。



  1. REDIS_ENCODING_INTは非垞に単玔です。 長い倀にキャストされた倀がLONG_MIN 、 LONG_MAXの範囲にある堎合、この圢匏で文字列を栌玍できたす。 したがっお、文字列「dict」はこの゚ンコヌディングの圢匏で正確に保存され、19526721000x74636964ずいう番号になりたす。 REDIS_SHARED_INTEGERSの範囲 redis.hで定矩され、デフォルトでは10000に等しいの事前に割り圓おられた特別な倀の範囲には、同じ゚ンコヌドが䜿甚されたす。 この範囲の倀は、倧根の始たりですぐに匷調衚瀺されたす。
  2. REDIS_ENCODING_EMBSTRは 、最倧39バむトの文字列 object.cの定数REDIS_ENCODING_EMBSTR_SIZE_LIMITの倀に䜿甚されたす。 これは、redisObjectずsds行を含む構造䜓が、アロケヌタによっお割り圓おられた単䞀のメモリ領域に配眮されるこずを意味したす。 これを念頭に眮いお、アラむメントを正しく蚈算できたす。 ただし、これは倧根のメモリフラグメンテヌションの問題ずその察凊方法を理解する䞊で重芁です。
  3. REDIS_ENCODING_RAWは 、長さがREDIS_ENCODING_EMBSTR_SIZE_LIMITを超えるすべおの行に䜿甚されたす。 この堎合、ptr *は、sds行のあるメモリ領域ぞの通垞のポむンタヌを栌玍したす。


EMBSTRは2012幎に登堎し、短い行で䜜業するずきにパフォヌマンスが60〜70向䞊したしたが、メモリぞの圱響ずその断片化に関する深刻な研究はただありたせん。


文字列「strings」の長さはわずか7バむトです。 その内郚衚珟のタむプはEMBSTRです。 この方法で䜜成された行は、次のようにメモリに配眮されたす。

 +--------------+--------------+------------+--------+----+ | robj data... | robj->ptr | sds header | string | \0 | +--------------+-----+--------+------------+--------+----+ | ^ +-----------------------+
      
      





これで、文字列「strings」を栌玍するために必芁なRAMの量を蚈算する準備が敎いたした。

4 + 4 * + 8゚ンコヌド+ 8lru+ 8refcount+ 8ptr+ 16sdsヘッダヌ+ 7行自䜓+ 1終端れロ= 56バむト。



redisObjectの型ず倀は、同じ数字の䞋䜍4ビットず䞊䜍4ビットのみを䜿甚するため、アラむメント埌のこれら2぀のフィヌルドは8バむトを䜿甚したす。



錻で運転しおいないこずを確認したしょう。 ゚ンコヌドず倀を芋おみたしょう。 行のデバッグにあたり知られおいないコマンドを1぀䜿甚したす-DEBUG SDSLEN。 ちなみに、このコマンドは公匏ドキュメントには含たれおいたせんが、 redis 2.6で远加されたため、非垞に䟿利です。

 set key strings +OK debug object key +Value at:0x7fa037c35dc0 refcount:1 encoding:embstr serializedlength:8 lru:3802212 lru_seconds_idle:14 debug sdslen key +key_sds_len:3, key_sds_avail:0, val_sds_len:7, val_sds_avail:0
      
      





䜿甚される゚ンコヌディングはembstrで、文字列の長さは7バむトval_sds_lenです。 hashedin.comの人たちが話しおいた96バむトはどうですか 私の理解では、それらは少し間違っおいお、「set foo bar」を䜿甚した䟋では112バむトのRAM倀ごずに56バむト、キヌごずに同じ量が必芁であり、そのうち106はオヌバヌヘッドです。



もう少し高く、BITMAPを䜿甚した堎合のメモリのフェヌドに぀いおの話を玄束したした。 私が話したい機胜は、それを䜿甚しおいる䞀郚の開発者の泚意から挏れおいたす。 みんな、蚘憶の最適化に関するコンサルタントはこれで定期的に皌いでいたす。 redis-labsやdatadogなど。 「ビットおよびバむトレベルの操䜜」コマンドファミリはredis 2.2で登堎し、すぐにリアルタむムカりンタヌたずえばSpoolの蚘事 の呜の恩人ずしお䜍眮付けられ、メモリを節玄できたす。 メモリを最適化するための公匏ガむドには、このデヌタファミリを䜿甚しおオンラむンで保存するこずに関する広告スロヌガンもありたす。「1億人のナヌザヌにずっお、このデヌタはRAMをわずか12メガバむトしか占有したせん。」 説明では、 SETBITずSETRANGEは 、メモリを割り圓おるずきにサヌバヌラグが発生する可胜性があるこずを譊告したすが、重芁なこずは省略しおいたすが、「BITMAPを䜿甚すべきではない堎合」たたは「BITMAPの代わりにSETを䜿甚する方がよい堎合」セクションを参照しおください。



ラむンが倧根でどのように成長するかを理解するず、そのビットマップを芋るこずができたす



䟋を考えおみたしょう。 最倧1,000䞇人が登録されおおり、1,000䞇人目のナヌザヌがオンラむンであるずしたす。

 setbit online 10000000 1 :0 debug sdslen online +key_sds_len:6, key_sds_avail:0, val_sds_len:1250001, val_sds_avail:1048576
      
      





実際のメモリ消費量は2,298,577バむトで、1,250,001バむトが「有甚」です。 ナヌザヌの1人のストレヌゞのコストは玄2.3 MBです。 SETを䜿甚する堎合、〜64バむト4バむトのペむロヌドを含むが必芁になりたす。 デヌタのたばらさを枛らし、ビットマップが30の範囲に収たるように、適切な集玄間隔を遞択する必芁がありたす。この堎合、実際にこのデヌタ構造にメモリを効果的に䜿甚したす。 これは、数癟䞇人の芖聎者がいお、10,000〜100,000人がオンラむンで芖聎しおいる堎合、ビットマップをこの目的で䜿甚するずメモリのオヌバヌヘッドになる可胜性があるずいう事実に基づいおいたす。



最埌に、倧根のラむンのサむズ倉曎は、メモリブロックの䞀定の再割り圓おです。 メモリの断片化は、倧根の別の特異性であり、開発者はほずんど考えたせん。

 info memory $222 # Memory used_memory:506920 used_memory_human:495.04K used_memory_rss:7565312 used_memory_peak:2810024 used_memory_peak_human:2.68M used_memory_lua:36864 mem_fragmentation_ratio:14.92 mem_allocator:jemalloc-3.6.0
      
      





mem_fragmentation_ratioメトリックは、オペレヌティングシステムによっお割り圓おられたメモリ used_memory_rss ず倧根によっお䜿甚されたメモリ used_memory の関係を瀺したす。 同時に、 used_memoryずused_memory_rssには、デヌタ自䜓ず、保存および衚瀺甚の内郚倧根構造の保存コストの䞡方が既に含たれおいたす。 倧根はRSSResident Set Sizeをオペレヌティングシステムによっお割り圓おられたメモリの量ず芋なしたす。メモリがオペレヌティングシステム自䜓によっお物理的に割り圓おられた堎合、ナヌザヌデヌタおよびその内郚プレれンテヌションのコストに加えお、断片化コストも考慮されたす。



mem_fragmentation_ratioを理解する方法は 倀2.1は、デヌタストレヌゞに必芁以䞊に210のメモリを䜿甚するこずを瀺しおいたす。 1未満の倀は、メモリが䞍足しおおり、オペレヌティングシステムがスワップするこずを瀺したす。



実際には、mem_fragmentation_ratioの倀が1〜1.5の範囲倖にある堎合は、䜕か問題があるこずを意味したす。 詊しおください





フラグメンテヌションに぀いお話すずき、LRUをオンにした倧根の詳现や、通垞の文字列キヌが倚数ある堎合の远加の問題を考慮したせん。これらはすべお別の蚘事にたずめられおいたす。 あなたがそれに぀いお曞く䟡倀があるかどうか、そしお倧根で䜜業するずきにあなたにずっお他に重芁だず思われる提案を共有しおくれたら感謝したす。



ナヌザヌpansaは、スワップ状況では、オペレヌティングシステムがプロセスにRAMの䞀郚を返した埌、倧根がused_memory_rssの倀を再蚈算しないこずを正しく芳察したす。 倧根は、デヌタにアクセスするずきにこの倀をすでに再蚈算したす。



目次



参考のための远加資料




All Articles