Skip to content

Paginated Local Storage

bonybeat edited this page Aug 15, 2014 · 15 revisions

Paginated Local Storage("plocal"と呼びます)は、ページモデルのディスクベースのストレージです。

plocalストレージは、**ディスクキャッシュ**を扱ういくつかのコンポーネントから構成されています。

以下はplocalストレージコンポーネントの一覧と簡単な説明です。

  1. クラスター は2種類のファイルから出来ています:
  • .pcl クラスターデータのファイル
  • .cpm レコードのクラスターと物理的な位置のマッピングのファイル
  1. Write Ahead (operation) Log (WAL) は2種類のファイルから出来ています:
  • .wal WALログの内容です
  • .wmr ストレージキャッシュとディスクの同期オペレーションのタイミングの情報
  1. SBTree Index, .sbtという拡張子のファイル
  2. Hash Index, .hit.him.hib.という拡張子のファイル
  3. Index Containers 単一のエントリィの非ユニークインデックス(Index RID Set)を持っています、拡張子は**.irs**
  4. File mapping, ファイル名とファイルidsのマッピング。name_id_map.cmという名前のファイル

ファイルシステム

plocalはディスクベースです。すべてのキャッシュページは物理ディスクにフラッシュされます。通常ディスク、SSD、フラッシュメモリーなど、マウントできるものなら利用可能です。

クラスター

クラスター レコードを格納するディスク領域の論理的な単位です。各クラスターはページに分割されています。ページは、クラスターで利用される最小の単位です。

各ページはシステム情報とレコードデータを持ちます。システム情報は、crc32チェックサムとその検証用マジックナンバーです。このシステム情報は、DBクラッシュ時のデータ整合性に使われます。コンソールから"check database"コマンドで起動することもできます。

各クラスターは二つのサブコンポーネントを持ちます:

  • データファイル(拡張子 .pcl )
  • データファイル中の物理位置とクラスターポジションのマッピング(拡張子 .cpm )

ファイルシステム

クラスターに対するアクセスを高速化するためには、SSD等の高速なディスクを推奨します。マウントしてSSDに対して、シンボリックリンクを使うのも良いでしょう。

クラスターポインター

データファイルと物理的な位置のマッピングはリストで管理されています。このリストの各エントリィは、データファイル中の物理的なレコード位置へのポインターで、サイズは固定です。

データファイルはページ分割されてて、ポインターは、ページインデックス(long値)とページ中のレコードの位置(int値)という二つの項目から構成されています。各レコードポインターは12バイトです。

クラスター中に新しいレコードを作成する

新しくレコードがINSERTされると、新しいポインターがリストに追加され、このポインターのインデックスがクラスターポジションになります。 このリストは追加だけが行われます。つまり、新しいレコードを追加すると、そのクラスターポジションは一意であり、再利用はされません。

クラスター中にレコードを削除する

レコードが削除されると、ページインデックスとレコードポジションは-1にセットされます。これはレコードが削除されたことを示します。uuidのように、それは二度と再利用されません。

通常、レコードが削除されても、ほとんど領域は減りません。定期的なエクスポート/インポートによる「オフライン圧縮」をしてもよいかもしれません。 エクスポート/インポートすると、レコードのクラスターポジションは変更されます(削除されていたデータはインポートされないので)。

レコードIDの移行

OrientDBのインポートツールは、手動でのハッシュインデックスを使って(デフォルト名は '___exportImportRIDMap')、古いレコードIDを新しいレコードIDにマップします。

Write Ahead (operation) Log (WAL) 書き込み先行ログ

WALは突然のシャットダウン(クラッシュ)後のレストアに必要です。

  • OrientDBプロセスのHard kill
  • 稼働中のJVMクラッシュ、ダウン
  • 稼働中のOSのクラッシュ、ダウン

plocalコンポーネントは、レコードの操作を完了する前に、その内容をWALにロギングします。WALは追加だけがおこなれます。WALにはストレージに対して行われたすべての変更操作の情報が記録されています。

WAL のディスクへのフラッシュ

WALは以下のタイミングで、そのメモリー上のバッファをディスクにフラッシュします

  • バックグランドスレッドで1秒置き。この間隔はstorage.wal.commitTimeoutプロパティで変更可能です。
  • storage.wal.cacheSizeプロパティで指定されたバッファ長(デフォルト65MB 変更可能)を超えた時

結果として、OrientDBがクラッシュした場合、最大1秒間分のデータ更新が失われる可能性があります。この間隔は、パフォーマンスと永続性のトレードオフになります。

WAL を別のディスクに配置する

WALはデータベースを置いているディスクと、同じディスクに配置しないようにしてください。別の場所に配置すると、パフォーマンスが向上します。storage.wal.pathプロパティにWALファイルの場所を指定できます。

インデックスはWALをどう使うのか?

インデックスへの変更はWALとどう関係するかについては二つのモードがあります。

  • ROLLBACK_ONLY(ロールバック時のも反映) (こちらがデフォルト)
  • FULL(常に反映)

ROLLBACK_ONLYモードでは、トランザクションをロールバックする際だけに、インデックス更新内容がWALにロギングされます。クラッシュした場合は、インデックスの内容はWALに 記録されていないので、クラッシュ後の再起動時に、インデックスは自動的にリビルドされます。FULLモードでは、クラッシュ後の再起動で、インデックスもWALからレストアされます。 この設定はindex.txModeプロパティで変更できます。

WALに関しては、**こちら**を参照して下さい。

ファイルタイプ

PLocalストレージはデータベースの内容をいくつかのファイルで管理しています。

  • .cpm、 物理ポジションとクラスターポジションのマッピング
  • .pcl、 データファイル
  • .sbt、 インデックスファイル
  • .wal.wmr、WALファイル
  • .cm、 ファイルとファイル名のマッピング
  • .irs、 非ユニークインデックスのためのレコードID

内部的にどのように動いているのか

基本的に、plocalストレージは、WALと一緒に動作する、2レベルのディスクキャッシュです。

すべてのファイルはページに分割され、各ファイルのオペレーションはページレベルで行われます。2レベルのディスクキャッシとは以下のようなものです。

  1. メモリー上で頻繁にアクセスされるページをキャッシュする
  2. アクセスが少ないページと多いページを自動的に分離して、少ないページをキャッシュアウトする
  3. 書き込み時にハードディスクのヘッドのシーク動作を極力減らす
  4. データの書き込みに対する負荷がひどく高くないようなケースで、すべての変更または追加されたページをバックグラウンドスレッドでディスクにフラッシュすることで、データ書き込みのための処理中断の必要性を減らす
  5. WALと一緒に動作して、単一ページへの書き込みをatomic(原子的な)オペレーションとして扱う

2レベルのディスクキャッシは、Readキャッシュ ( 2Q キャッシュアルゴリズムに基づいている) と Writeキャッシュ ( WOWi キャッシュアルゴリズムに基づいている)によって、構成されています。

すべてのファイルに対する一連のオペレーションは以下のようになります。

  1. OReadWriteDiskCache#openFileによって、ファイルをオープンして、そのIDを取得します。ファイルが無ければ自動的に作成する。ファイルのIDは、特別なメタデータに記録されて、ファイルが削除されない限り変わりません。
  2. OReadWriteDiskCache#allocateNewPageであたらしいページをAllocateするか、ORreadWriteDiskCache#loadで、既存のページをヒープメモリーにロードします。
  3. OCacheEntry#getCachePointer()で、オフヒープメモリーのAllocateされたエリアへのポインターを取得します。
  4. ページを変更する場合は、writeロックか(データを読んでいる場合は)readロックを取得する必要があります。writeロックは、writeキャッシュ用のバックグラウンドスレッドによってページがディスクへキャッシュアウトされる際に、一貫性を損なうことを防ぐために必要です。
  5. オフヒープメモリーのデータを更新または読み込む
  6. 必要ならば、OCachePointer#releaseExclusiveLockによりwriteロックを取得します。
  7. ページデータの変更が必要ならダーティーページとしてマーキングします。これにより、本当に変更されたページ(ダーティーページ)だけ、writeキャッシュがフラッシュできるようになります。OCacheEntry#markDirty。
  8. レコードをディスクキャッシュに書き戻す。言い換えると、キャッシュに対して、このページはもう使わないので、他のページ用のエリアを確保するために、メモリーから追い出せることを教えます。OReadWriteDiskCache#release。

キャッシュイン・キャッシュアウトのアルゴリズム

最初にページをロードする時は、Readキャッシュはある種のLRUキューのように見えます。 LRUキューは、アクセス数が少ないされたページや長い間アクセスされないページ用のもの(キャッシュ用容量の25%)と、長い間、非常に頻繁にアクセスされたページ用のもの(キャッシュ用容量の75%)の二つがあります。

ページがReadキャッシュ用のLRUキューに無ければ、ReadキャッシュはWriteキャッシュに、ディスクからデータをロードするように依頼します。

もし、Writeキャッシュにあればそれが使われるし、無ければディスクからWriteキャッシュにロードされます。

データがWriteキャッシュによって、ディスクから読み込まれた時、“short living”ページ用のLRUキューに置かれます。 このページが、時間が経過して、頻繁にアクセスされるようになったら、ページは“long living”ページ用のLRUキューに移動します。

Writeキャッシュは、すべてのページがディスク上の位置によって整列したリングバッファだと考えることができます。 これは、ページフラッシュ中のハードディスクのヘッドの動きを最小にするための手法でです。 また、ページは、バックグランドスレッドによって常にフラッシュされ続けています。 このアプローチは、十分なメモリーが積まれていて、ディスクへのフラッシュがバックグラウンドでしか動かないケースで、I/Oボトルネックの減少に効果的です。

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script> $(function() { $("*").contents().filter(function() { return this.nodeType==8 && this.nodeValue.match(/^original/); }).each(function(i, e) { var tooltips = e.nodeValue.replace(/^original *[\n\r]|[\n\r]$/g, ''); $(this).prev().attr('title', tooltips); }); }); </script>
Clone this wiki locally