RocksDBの決意


 

1.はじめに

RocksDBプロジェクトは完全にストレージデバイスのこのタイプの可能性をタップしながら、積載できる効率的なデータベースの実装を開発することを望んで高速なストレージデバイス(特にフラッシュ)上のデータ・サービス・サーバを格納し、Facebookの実験に由来しました。RocksDBは、データを格納するためのC ++ライブラリで、原子読み取りKVをサポートしています。RocksDBは、コンフィギュレーションでより高い柔軟性を実現し、純粋なメモリ、フラッシュ、HDDまたはHDFSを含むさまざまな環境を生成するために実行することができます。圧縮アルゴリズムの多様性と生産支援やデバッグのためのさまざまなツールをサポートしていRocksDB。RocksDBは心の中で多くのLevelDBコードとApacheのHBaseを借りました。当初ベースLevelDB1.5開発。

RocksDBが記憶されている埋め込みKV(任意のバイトストリーム)です。すべてのデータは、(キー)に入れ、(キー)を取得サポートすることができます整然としたエンジンに格納され、削除(キー)とNewIterator()。基本組成RocksDBはmemtable、sstfileおよびログファイルです。memtableは、メモリデータ構造でデータが最初memtableにおける書き込み要求に書き込まれ、その後、必要に応じてログファイルを書き込みます。ログファイルは、シーケンシャル書き込みファイルです。メモリオーバーフローテーブル、データは、このmemtable対応するログファイルを安全に削除するsstfileへのフラッシュであろう。データはまた、見つけるのは簡単sstfileストレージを命じました。

RocksDBは、キーと値は制限なし完全バイトストリーム、キーサイズおよび値です。ユーザインタフェースはDB、MultiGetクエリ機能からクエリ処理に対応するキー値の一種類は、バルクを提供する提供を受けます。すべてのデータDBがキー方式は、ユーザー定義と比較することができる、請求鍵保管、に従って順序付けられます。RangeScanは、第1の特定のキーにシークし、この点からトラバースを開始するユーザ機能を提供するイテレータ方法。イテレータもイテレータを実行する際の順序トラバーサルを逆RangeScan達成することができ、ユーザが時点の一貫したビューを見ます。

Torleranceフォールト     

チェックサムによって検出されたRocksDBディスクのデータの破損。各データブロック(4K-128K)のためのSSTファイルは、対応するチェックサム値を有しています。データ・ブロックに格納されたコンテンツを変更することはできません書きます。

マルチスレッドコンパクション     

ユーザが繰り返しキーを書き込むと、DBのキー値が複数存在なり、圧縮操作は、冗長データを除去するための鍵です。キーが削除された場合、compationはまた、ユーザ設定は、compation動作はマルチスレッド実行することができ、適切な場合、実際に、データの基礎となる削除ジョブを実行するために使用することができます。(重複を除去し、無効なデータを削除する)メモリ・データ・テーブルがいっぱいになったとき、データをメモリL0ファイルに書き込まれる、データDBのsstfileに格納されています。時々小さなデータファイルがcompationある大きなファイル、に再マージされます。LSM書き込みスループット性能は、データをRAMまたはSSDに記憶され、特にエンジンcompation、に直接依存します。RocksDBまた、マルチスレッド並列圧縮をサポートしています。すべてのバックグラウンドスレッドが圧縮を実行しているメモリ記憶装置にデータをフラッシュするために使用される背景圧縮スレッドは、書き込み操作の瞬時多数はすぐに停止を書く引き起こす可能性がありますメモリテーブルで満たされます。より少ないスレッドが、データフラッシュ動作を実行するように構成することができます

ブロックキャッシュ - 圧縮および非圧縮データ     

RocksDBは、提供LRUキャッシュブロックの読み取りサービスを使用します。圧縮されていないキャッシュデータRAM、他の1つのキャッシュRAMデータ圧縮することができる二つの別々のキャッシュにブロックキャッシュパーティション。圧縮キャッシュ構成が開かれた場合、ユーザーは、再キャッシュキャッシュOSを避ける​​ためにも、同じ圧縮データ、一般的に開いて直接IOであるだろう。

利用可能な構成  

DBOptions、ColumnFamilyOptions、BlockBasedTableOptions、またはPlainTableOptionsを:オプション文字列またはオプションマップかどうかは、オプション名は、これらには、ターゲットクラスの変数名です。options.h情報に記載されているような変数名とDBOptionsとColumnFamilyOptionsに見出すことができ、BlockBasedTableOptions、可変情報及びPlainTableOptionsはでtable.hに見出すことができます。設定項目のほとんどはオプション文字列とオプションのマップをサポートすることができますが、いくつかの例外がまだあることに留意すべきです。すべての設定項目RocksDBサポートがdb_options_type_info、cf_options_type_infoとblock_based_table_type_infoで見つけることができ、ソースファイルはutilの/ options_helper.hです。

LSM-ツリー

RocksDBは、以下のようおそらくLSM-ツリーに基づいています

まず第一に、任意の書き込みが最初(Memtable)WALは、その後、メモリテーブルを書き書いたでしょう。パフォーマンスのためのコースのうち、あなたはWALを書き込むことはできませんが、それはデータを失うの崩壊の危険に直面する可能性があります。ユーザーが実際のビジネス・シナリオを選択することができ、メモリテーブルskiplistサポートは通常、同時書き込みですが、RocksDBはまた、様々なskiplistをサポートしています。

Memtableそれは不変Memtableなり、充填された後、バックグラウンドでRocksDBは、レベル0層でソート文字列テーブル(SST)ファイルを生成し、ディスクへのこのMemtableフラッシュスルーフラッシュスレッドであろう。SSTレベル0のファイル数が閾値レベルを超えたときに、それはそうで圧縮ポリシーの階層レベル1に入ると、あろう。

圧縮は、その後、非常に高速な書き込みが、減少した読み取り性能になりますされていない場合、ここでの鍵は、圧縮にあり、同じスペースを拡大することは非常に深刻な問題を引き起こします。書き込みのバランスをとるために、読んで、空間的な問題は、RocksDB圧縮がバックグラウンドで実行され、SSTの異なるレベルをマージします。圧縮ではなく、コストをかけずに、それは私を取るだろう/ O、書き込みと読み出し動作の外に影響を与えるにバインドされています。

RocksDBのために、彼は1つがデフォルトの平準化圧縮され、3つの圧縮戦略を持って、別のは、多くの場合、サイズ-疲れコンパクションが言われているユニバーサル圧縮、である、種類FIFO圧縮があります。FIFOの場合は、その戦略は非常に単純であるしきい値を超えた場合は、レベル0のすべてのSSTは、彼らはSSTの古いものから削除し始め、実際にあなたが見ることができ、このメカニズムは、時系列データを格納するために非常に適しています。

RocksDB実用上は、それが実際に使用される他の層を平坦化するながらハイブリッド戦略は、レベル0の層に、それは、実際のサイズ、疲れです。

ここでセンスアンプはRAとみなすことができる=などのユーザーとしてクエリの数*ディスクを読み込むには、ページを読みたいが、実際、LSMのために、我々はズーム、ズームでの作成を検討する必要があり、スペースを拡大読んで、いくつかの増幅率について話をします以下は、3つのページを読んで、その後、センスアンプは3です。増幅は、書き込み増幅は10であり、100のバイトを10のバイトを書き込むようなユーザとして、データベースに書き込まれたディスク/データにwriteen WA =データが書き込まれているが、実際にディスクに書き込まれています。空間増幅用として、それはデータベースファイル/ディスク上で使用されるデータベースのサイズのSA =大きさ、つまり、データベースが100メガバイトであってもよいが、実際にはスペースの200メガバイトを占め、次いで、空間が2に拡大しています。

 


 

2.締固め

LSMツリー可能な離散ランダム書き込み要求は、書き込み性能を向上させるために、書き込み要求(WAL +コンパクション)の逐次バッチに変換されます。しかし、それはまた、いくつかの問題をもたらします:

  • センスアンプ(増幅を読みます)。LSM-ツリーを使用すると、目的のデータを見つけるまでの動作は、見つけるために、層によって古い(上から下)層への新たなニーズ読みます。複数のI / Oを取ることがあり、このプロセス 特に範囲クエリの場合、影響は明らかです。
  • 拡大した空間(スペース増幅)。すべての書き込みがシーケンシャル書き込み(追加のみ)ではなく、インプレース更新されているので、とても古くなったデータはすぐに離れてクリアされません。

RocksDBとLevelDBは背景を読むことによって増幅圧縮を減らすために、空間増幅(古いデータをクリーンアップ)、それもたらした書き込み増幅(SSTは、ファイルの数を減らす)の問題(増幅を書きます)。

  • 増幅を書きます。そして、プログラムのデータサイズは、実際にデータの書き込みのHDD / SSDに必要なサイズを書かれました。通常、データが書き込まれたプログラムよりも上層に観察HDD / SSDデータを書き込まれます。

倍のストレージの主流としてHDDでは、RocksDB書き込み増幅の問題によって引き起こされる圧縮は非常に明白ではありません。これは、次のとおりです。

  • HDDのシーケンシャル読み取りと書き込みのパフォーマンスは、書き込み増幅のコストがもたらす相殺するのに十分な、はるかに優れたランダム読み取りと書き込みのパフォーマンスです。
  • HDDの書き込み量は、実質的に自分の人生に影響を与えません。

今主流のSSDストレージになって、書き込み増幅によって引き起こされる圧縮は、ますます深刻になります:

  • SSDのシーケンシャル読み取りと書き込みのパフォーマンスは、いくつかのランダム読み取りおよび書き込みのパフォーマンスよりも優れているが、ギャップはそれほど大きなHDDではありません。したがって、メリットに比べてシーケンシャル書き込みランダム書き込みでは、これが問題である、書き込み増幅のコストがもたらす相殺することはできません。
  • SSDの寿命と増幅を書き、量について書くが大幅にSSDの寿命が短くなりますあまりにも深刻です。SSDは書き込みをサポートしていないのでカバーは、書き換え可能に(消去)消去されなければなりません。各SSDブロック(ブロックSSDは、消去動作の基本単位である)、消去の平均数は限られています。

だから、SSD上で、書き込み増幅のLSM-Treeが非常に興味深い質問です。書き込み増幅、3つのCAP定理として、のように、トレードオフを行う必要があり、スペースを拡大し、拡大読みください。

RocksDB 書き込み増幅分析:

1 - 書き込みREDOログ

1 - ファイルL0に書き込ま不変Memtable

+2 - L0とL1コンパクション(キーファイルSST範囲L0一般にL0とL1のデータサイズを維持しようとすると、パフォーマンス上の理由から、重複している同じであり、データは、全データ量とL1 L0を取るたびにコンパクションの合計量であります)

+11 - LN-1とLnの組み合わせライト(N> = 2、デフォルトでは、データサイズがLnはLN-1 10倍、max_bytes_for_level_multiplierを参照されたいです)。

7回 - したがって、総書き込み増幅は4 + 11 *(N-1)= 11 * Nです。キーは、nの値です。

max_bytes_for_level_multiplierデフォルト値10を仮定し、nの値は、大きさL1と衝撃LSMツリーのサイズを受けます。

サイズmax_bytes_for_level_baseによって決定さL1は、デフォルトでは、256 MBです。

L0とL1と同じ大きさのデフォルトのサイズでは、それは256メガバイトです。しかし、L0特別、SSTファイルL0の数はL0をトリガー、level0_file_num_compaction_triggerに達したとき - > comapctionのL1。だから、最大サイズL0はwrite_buffer_sizeは* min_write_buffer_number_to_merge * level0_file_num_compaction_triggerです。

write_buffer_size默认64メガバイト

min_write_buffer_number_to_merge默认1

level0_file_num_compaction_triggerデフォルト4

だから、デフォルトL0により、64メガバイトまで* 1 * 4 = 256メガバイト

次のようにそのため、各層のRocksDBのデフォルトサイズは、次のとおり

L0 - 256メガバイト

L1 - 256メガバイト

L2 - 2.5ギガバイト

L3 - 25ギガバイト

L4 - 250ギガバイト

L5 - 2500ギガバイト

平らに圧縮VS階層型締固め

我々は、すべての書き込みは、実際よりフレンドリーで、LSMのために、それはSSTファイルを形成し、バックグラウンドでディスクへのフラッシュ、その後、内部memtable最初に書き込まれます、知っているが、読んだときにすべきですSSTファイルのすべてを通過する可能性があり、このオーバーヘッドは非常に大きいです。それはスペースを取る、内部滞在LSMで複数のバージョンを持っていますので、同時に、LSMは、マルチバージョニングメカニズムであり、キーは、頻繁に更新することができます。

通常は、LSM固め、この2つの問題を解決するために、LSMは、バックグラウンドのコンパクト化になり、つまり、SSTは、パフォーマンスを向上させるために、ファイルを再配置するスペースの不要なリリースバージョンを読み出し、他の一方で二つの方法、1は階層型であるが、そこにありますそれは平らです。

特許文献1は、設定された閾値に達するように、図は、2つの圧縮区別、レベル1にブラシレベル0、レベルSSTあり、それは圧縮のために必要です。階層化のために、我々はすべてのレベル1レベル2 SSTがレベル2に一つにマージファイルをします。これは、階層型、それのために、圧縮は実際に少しSSTは、より低SSTプロセスにマージすべてのトップである、です。

平準化のために、SST内の異なるレベルが同じサイズである、結局SSTを形成し、一緒にSSTオペレータレベル2マージを用いて行われるレベル1、レベル2の各SSTを注文し、重なりません。

上面仅仅是一个简单的介绍,大家可以参考 ScyllaDB 的两篇文章 Write Amplification in Leveled Compaction,Space Amplification in Size-Tiered Compaction,里面详细的说明了这两种 compaction 的区别。

 


 

3. Block Cache

Block Cache是RocksDB把数据缓存在内存中以提高读性能的一种方法。开发者可以创建一个cache对象并指明cache capacity,然后传入引擎中。cache对象可以在同一个进程中供多个DB Instance使用,这样开发者就可以通过配置控制所有的cache使用。Block cache存储的是非压缩的数据块内容。用户也可以设置另外一个block cache来存储压缩数据块。读数据时首先从非压缩数据块cache中读数据、然后读压缩数据块cache。当Direct-IO打开的话,压缩数据库可以作为系统页缓存的替代。RocksDB中有两种cache的实现方式,分别为LRUCache和CLockCache。这两种cache都会被分片,来降低锁压力。用户设置的容量平均分配给每个shard。默认情况下,每个cache都会被分片为64块,每块大小不小于512K字节。

LRU Cache

默认情况,RocksDB使用LRU Cache,默认大小为8M。cache的每个分片都有自己的LRU list和hash表来查找使用。每个shard都有个mutex来控制数据并发访问。不管是数据查找还是数据写入,线程都要获取cache分片的锁。开发中也可以调用NewLRUCache()来创建一个LRU cache。这个函数提供了几个有用的配置项来设置cache:

Capacity               cache的总大小

num_shard_bits               去cache key的多少字节来选择shard_id。cache将会被分片为2^num_shard_bits

strict_capacity_limit        很少会出现block cache的size超过容量的情况,这种情况发生在持续不断的read or iteration 访问block cache,pinned blocks的总大小会超过容量。如果有更多的读请求将block数据写入block cache时,且strict_capacity_limit=false(default),cache服务会不遵循容量限制并允许写入。如果host没有足够内存的话,就会导致DB instance OOM。如果将这个配置设置为true,就可以拒绝将更多的数据写入cache,fail掉那些read or iterator。这个参数配置是以shard为控制单元的,所以会出现某一个shard在capcity满时拒绝继续写入cache,而另一个shard仍然有extra unpinned space。

high_pri_pool_ratio        为高优先级block预留的capacity 比例

Clock Cache

  ClockCache实现了CLOCK算法。CLOCK CACHE的每个shard都有一个cache entry的圆环list。算法会遍历圆环的所有entry寻找unspined entry来回收,但是如果上次scan操作这个entry被使用的话,也会有继续留在cache中的机会。寻找并回收entry使用tbb::concurrent_hash_map。

  使用LRUCache的一个好处是有一把细粒度的锁。在LRUCache中,即使是查找操作也需要获取分片锁,因为有可能会更改LRU-list。在CLock cache中查找并不需要获取分片锁,只需要查找当前hash_map就可以了,只有在insert时需要获取分片锁。使用clock cache,相比于LRU cache,写吞吐有一定提升。

当创建clock cache时,也有一些可以配置的信息。

    Capacity               same as LRUCache

    num_shard_bits              same as LRUCache

    strict_capacity_limit       same as LRUCache

Simulated Cache

SimCache是当cache capacity或者shard num发生改变时预测cache hit的方法。SimCache封装了真正的Cache 对象,运行一个shadow LRU cache模仿具有同样capacity和shard num的cache服务,检测cache hit和miss。这个工具在下面这种情况很有用,比如:开发者打开了一个DB 实例,配置了4G的cache size,现在想知道如果将cache size调整到64G时的cache hit。

SimCache的基本思想是根据要模拟的容量封装正常的block cache,但是这个封装后的block cache只有key,没有value。当插入数据时,把key插入到两个cache中,但是value只插入到normal cache。value的size会在两种cache中都计算进去,但是SimCache中因为只有key,所以并没有占用那么多的内存,但是以此却可以模拟block cache的一些行为。

 


 

4. MemTable

MemTable是一种在内存中保存数据的数据结构,然后再在合适的时机,MemTable中的数据会flush到SST file中。MemTable既可以支持读服务也可以支持写服务,写操作会首先将数据写入Memtable,读操作在query SST files之前会首先从MemTable中query数据(因为MemTable中的数据一直是最新的)。一旦MemTable满了,就会转换为只读的不可改变的,然后会创建一个新的MemTable来提供新的写操作。后台线程负责将MemTable中的数据flush到SST file,然后这个MemTable就会被销毁。

重要的配置:

memtable_factory:memtable的工厂对象。通过这个工厂对象,用户可以改变memtable的底层实现并提供个性化的实现配置。

write_buff_size :单个内存表的大小限制

db_write_buff_size: 所有列族的内存表总大小。这个配置可以管理内存表的总内存占用。

write_buffer_manager : 这个配置不是管理所有memtable的总内存占用,而是,提供用户自定义的write buffer manager来管理整体的内存表内存使用。这个配置会覆盖db_write_buffer_size。

max_write_buffer_number:内存表的最大个数

memtable的默认实现是skiplist。除了默认memtable实现外,用户也可以使用其他类型的实现方法比如 HashLinkList、HashSkipList or Vector 来提高查询性能。

Skiplist MemTable

基于Skiplist的memtable在支持读、写、随机访问和顺序scan时提供了较好的性能。此外,还支持了一些其他实现不能支持的feature比如concurrent insert和 insert with hint。

HashSkiplist MemTable

如其名,HashSkipList是在hash table中组织数据,hash table中的每个bucket都是一个skip list,HashLinkList也是在hash table中组织数据,但是每一个bucket是一个有序的单链表。这两种结构实现目的都是在执行query操作时可以减少比较次数。一种使用场景就是把这种memtable和PlainTable SST格式结合在一起,然后将数据保存在RAMFS中。当执行检索或者插入一个key时,key的前缀可以通过Options.prefix_extractor来检索,之后就找到了相应的hash bucket。进入到 hash bucket内部后,使用全部的key数据来进行比较操作。使用hash实现的memtable的最大限制是:当在多个key前缀上执行scan操作需要执行copy和sort操作,非常慢且很耗内存。

flush

在以下三种情况下,内存表的flush操作会被触发:

内存表大小超过了write_buffer_size

全部列族的所有内存表大小超过了db_write_buffer_size,或者wrtie_buffer_manager发出了flush的指令。这种情况下,最大的内存表会被选择进行flush操作。

全部的WAL文件大小超过max_total_wal_size。在这种场景下,内存中数据最老的内存表会被选择执行flush操作,然后这个内存表对应的WAL file会被回收。

所以,内存表也可以在未满时执行flush操作。这也是产生的SST file比对应的内存表小的一个原因,压缩是是另一个原因(内存表总的数据是没有压缩的,SST file是压缩过的)。

Concurrent Insert

如果不支持concurrent insert to memtable的话,来自多个线程的concurrent 写会顺序地写入memtable。默认是打开concurrent insert to memtable,也可以通过设置allow_concurrent_memtable_write来关闭。

 


 

5. Write Ahead Log

对RocksDB的每一次update都会写入两个位置:1) 内存表(内存数据结构,后续会flush到SST file) 2)磁盘中的write ahead log(WAL)。在故障发生时,WAL可以用来恢复内存表中的数据。默认情况下,RocksDB通过在每次用户写时调用fflush WAL文件来保证一致性。

 


 

6. Write Buffer Manager

Write buffer mnager帮助开发者管理列族或者DB instance的内存表的内存使用。

  • 管理内存表的内存占用在阈值内
  • 内存表的内存占用转移到block cache

  Write buffer manager与rate_limiter和sst_file_manager类似。用户创建一个write buffer manager对象,传入 column family或者DBs的配置中。可以参考write_buffer_manager.h的注释部分来学习如何使用。

Limit total memory of memtables

  在创建write buffer manager对象时,内存限制的阈值就已经确定好了。RocksDB会按照这个阈值去管理整体的内存占用。

  在5.6或者更高版本中,如果整体内存表使用超过了阈值的90%,就会触发正在写入的某一个column family的数据执行flush动作。如果DB instance实际内存占用超过了阈值,即使全部的内存表占用低于90%,那也会触发更加激进的flush动作。在5.6版本以前,只有在内存表内存占用的total超过阈值时才会触发flush。

  在5.6版本及更新版本中,内存是按照arena分配的total内存计数的,即使这些内存不是被内存表使用。在5.6之前版本中,内存使用是按照内存表实际使用的内存

Cost memory used in memtable to block cache

  从5.6版本之后,用户可以将内存表的内存使用的占用转移到block cache。不管是否打开内存表的内存占用,都可以这样操作。

  大部分情况下,block cache中实际使用的blocks远比block cache中的数据少很多,所以如果用户打开了这个feature后,block cache的容量会覆盖掉block cache和内存表的内存占用。如果用户打开了cache_index_and_filter_blocks的话,这三种内存占用都在block cache中。

  具体实现如下,针对内存表分配的每一个1M内存,WriteBufferManager都会在block cache中put一个dummy 1M的entry,这样block cache就可以正确的计算内部占用,而且可以在需要时淘汰掉一些block以便腾出内存空间。如果内存表的内存占用降低了,WriteBufferManager也不会立马三除掉dummmy blocks,而是在后续慢慢地释放掉。这是因为内存表空间占用的up and down太正常不过了,RocksDB不需要对此太过敏感。

  • 把使用的block cache传递给WriteBufferManager
  • 把WriteBufferManager的参数传入RocksDB内存表占用的最大内存
  • 把block cache的容量设置为 data blocks和memtables的内存占用总和

 

 

 


 

Ref:

Tuning RocksDB – Options    https://www.jianshu.com/p/8e0018b6a8b6

 

https://www.jianshu.com/u/aa9cae571502

https://www.jianshu.com/p/9b7437b5ea5b

https://zhuanlan.zhihu.com/p/37193700

 

おすすめ

転載: www.cnblogs.com/pdev/p/11277784.html