RocksDB の概要
ロックスDBは、キー/値ストアおよび読み取り/書き込み関数を提供する LSM ツリーstorageエンジンです。 Facebook によって開発され、LevelDB に基づいています。ユーザーによって書き込まれたキーと値のペアは、まず先行書き込みログ (WAL) に挿入され、次にメモリ内の SkipList ( MemTableと呼ばれるデータ構造) に書き込まれます。 LSM ツリー エンジンは、ランダムな変更 (挿入) を WAL ファイルへの順次書き込みに変換するため、B ツリー エンジンよりも優れた書き込みスループットを提供します。
メモリ内のデータが特定のサイズに達すると、RocksDB はその内容をディスク内の SST (Sorted String Table) ファイルにフラッシュします。 SST ファイルは複数のレベルで構成されます (デフォルトは最大 6 レベル)。レベルの合計サイズがしきい値に達すると、RocksDB は SST ファイルの一部を選択し、それらを次のレベルにマージします。後続の各レベルは前のレベルの 10 倍大きいため、データの 90% が最後のレイヤーに保存されます。
RocksDB を使用すると、ユーザーは複数のカラムファミリー (CF) を作成できます。 CF には独自の SkipList ファイルと SST ファイルがあり、同じ WAL ファイルを共有します。このように、アプリケーションの特性に応じて、異なる CF に異なる設定を持たせることができます。同時に WAL への書き込み数は増加しません。
TiKVアーキテクチャ
TiKV のアーキテクチャは次のように図示されています。
TiKV のstorageエンジンとして、RocksDB はRaftログとユーザー データの保存に使用されます。 TiKV ノード内のすべてのデータは 2 つの RocksDB インスタンスを共有します。 1 つはRaftログ (通常は raftdb と呼ばれます) 用で、もう 1 つはユーザー データと MVCC メタデータ (通常は kvdb と呼ばれます) 用です。 kvdb には、raft、lock、default、write の 4 つの CF があります。
- raft CF: 各リージョンのメタデータを格納します。占有スペースはごくわずかなので、ユーザーは気にする必要がありません。
- lock CF:悲観的トランザクションの悲観的ロックと分散トランザクションの Prewrite ロックを格納します。トランザクションがコミットされた後、ロック CF 内の対応するデータはすぐに削除されます。したがって、ロック CF 内のデータのサイズは通常非常に小さくなります (1 GB 未満)。ロック CF 内のデータが大幅に増加する場合は、多数のトランザクションがコミットを待機していることを意味し、システムにバグまたは障害が発生していることを意味します。
- write CF: ユーザーが実際に書き込んだデータと MVCC メタデータ (データが属するトランザクションの開始タイムスタンプとコミット タイムスタンプ) を保存します。ユーザーがデータ行を書き込むとき、データ長が 255 バイト未満の場合、そのデータは書き込み CF に保存されます。それ以外の場合は、デフォルトの CF に保存されます。 TiDB では、非一意のインデックスに格納されている値は空であり、一意のインデックスに格納されている値が主キー インデックスであるため、セカンダリ インデックスは書き込み CF のスペースのみを占有します。
- デフォルトの CF: 255 バイトを超えるデータを格納します。
RocksDB のメモリ使用量
読み取りパフォーマンスを向上させ、ディスクへの読み取り操作を減らすために、RocksDB はディスクに保存されているファイルを特定のサイズ (デフォルトは 64 KB) に基づいてブロックに分割します。ブロックを読み取るときは、まずデータがメモリ内の BlockCache にすでに存在するかどうかを確認します。 true の場合、ディスクにアクセスせずにメモリから直接データを読み取ることができます。
BlockCache は、LRU アルゴリズムに従って、最も最近使用されていないデータを破棄します。デフォルトでは、TiKV はシステムメモリの 45% を BlockCache に割り当てます。ユーザーは、 storage.block-cache.capacity
設定を自分で適切な値に変更することもできます。ただし、総システムメモリの 60% を超えることはお勧めできません。
RocksDB に書き込まれたデータは、まずMemTableに書き込まれます。 MemTableのサイズが 128 MB を超えると、新しいMemTableに切り替わります。 TiKV には 2 つの RocksDB インスタンスがあり、合計 4 つの CF があります。各 CF の 1 つのMemTableのサイズ制限は 128 MB です。同時に最大 5 つの MemTable が存在できます。それ以外の場合、フォアグラウンド書き込みはブロックされます。この部分が占有するメモリは最大 2.5 GB (4 x 5 x 128 MB) です。この制限を変更するとメモリの消費が少なくなるため、変更することはお勧めできません。
RocksDB のスペース使用量
- マルチバージョン: RocksDB は LSM ツリー構造を備えたキーと値のstorageエンジンであるため、 MemTable内のデータは最初に L0 にフラッシュされます。ファイルは生成された順に配置されるため、L0 の SST の範囲間に重複が存在する可能性があります。その結果、同じキーが L0 に複数のバージョンを持つ可能性があります。ファイルを L0 から L1 に結合すると、一定のサイズ (デフォルトは 8 MB) で複数のファイルに分割されます。同じレベルの各ファイルのキー範囲は重複しないため、L1 以降のレベルでは各キーのバージョンが 1 つだけ存在します。
- スペースの拡大: 各レベルのファイルの合計サイズは前のレベルの x (デフォルトは 10) 倍であるため、データの 90% が最後のレベルに保存されます。これは、RocksDB の空間増幅が 1.11 を超えないことも意味します (L0 にはデータが少ないため、無視できます)。
- TiKV の空間増幅: TiKV には独自の MVCC 戦略があります。ユーザーがキーを書き込むと、RocksDB に書き込まれる実際のデータは key + commit_ts です。つまり、更新と削除によって新しいキーも RocksDB に書き込まれます。 TiKV は古いバージョンのデータを定期的に削除します (RocksDB の削除インターフェイスを通じて)。そのため、ユーザーが TiKV 上に保存したデータの実際の容量は、1.11 に過去 10 分間に書き込まれたデータを加えたものに拡大すると考えられます。 (TiKV が古いデータを即座にクリーンアップすると仮定します)。
RocksDB のバックグラウンド スレッドとコンパクション
RocksDB では、 MemTableの SST ファイルへの変換や、さまざまなレベルでの SST ファイルのマージなどの操作がバックグラウンド スレッド プールで実行されます。バックグラウンド スレッド プールのデフォルトのサイズは 8 です。マシンの CPU の数が 8 以下の場合、バックグラウンド スレッド プールのデフォルトのサイズは CPU の数から 1 を引いたものになります。
一般に、ユーザーはこの構成を変更する必要はありません。ユーザーがマシン上に複数の TiKV インスタンスをデプロイしている場合、またはマシンの読み取り負荷が比較的高く書き込み負荷が低い場合は、必要に応じてrocksdb/max-background-jobs
~ 3 または 4 を調整できます。
ライトストール
ノート:
このセクションは TiDB に関するものであり、 TiDB Cloudには適用されません。
RocksDB の L0 は他のレベルとは異なります。 L0のSSTは世代順に並んでいます。 SST 間のキー範囲は重複する可能性があります。したがって、クエリの実行時には、L0 の各 SST を順番にクエリする必要があります。クエリのパフォーマンスに影響を与えないように、L0 にファイルが多すぎる場合、WriteStall がトリガーされて書き込みがブロックされます。
書き込み遅延が突然急激に増加した場合は、まず Grafana RocksDB KV パネルでWriteStall Reasonメトリックを確認します。 L0 ファイルが多すぎることが原因で WriteStall が発生した場合は、次の設定を 64 に調整できます。
rocksdb.defaultcf.level0-slowdown-writes-trigger
rocksdb.writecf.level0-slowdown-writes-trigger
rocksdb.lockcf.level0-slowdown-writes-trigger
rocksdb.defaultcf.level0-stop-writes-trigger
rocksdb.writecf.level0-stop-writes-trigger
rocksdb.lockcf.level0-stop-writes-trigger