H2Databaseを追っかけていたりしたブログ

H2 database のリリースノートを読んだりとか。

h2databaseを読む2

f:id:ysobj:20140526075951p:plain

H2のPageStoreでは、データベースのデータを1つのファイルで管理しており、この1つのファイルをページ単位(PAGE_SIZEで指定可能で、デフォルトは2048バイト。512バイトから32768バイトまでの2のべき乗サイズを指定可能です。)で管理します。この1ページがそのまま1つのorg.h2.store.Pageオブジェクトに対応します。

Pageは入っている中身により、別々の具象クラスがあります。

テーブルのデータはorg.h2.store.Pageを継承するorg.h2.index.PageData(PageDataNode/PageDataLeaf)型で管理されます。
インデックスと同様B-Tree構造になっています。
PageDataNodeはキーとページ番号だけが格納され、PageDataLeafにはキーと実際のテーブルのデータが格納されています。

ここで「キー」と言っているのは、いわゆるプライマリーキーではなく、内部的なキーのことです。プライマリーキーのないテーブルや、プライマリーキーが数値以外の型であってもここでのキーは必ずlong型となります。挙動として、プライマリーキーが数値の場合、プライマリーキーが内部のキーとしてそのまま使われるので効率的です。

なお、1つのPageDataの中には1つのテーブルのデータしか入りません。0行であっても、CREATE TABLEの時点でルートになるページが作成されるので、最低PAGE_SIZE分はデータファイルが拡張します。

org.h2.index.PageDataNode

自分の子供となるPageDataNode/PageDataLeafのページ番号とキーと、そのbyte表現(org.h2.store.Data)を保持しています(このbyte表現がそのままデータファイルのページのフォーマットです)

rootノードのみ、テーブル全体の行数を保持しています(rootノード以外のノードは行数は-1が入っている)。(H2でselect count(*) from TABLE が速いのはここの件数をみるだけだから)

org.h2.index.PageDataLeaf

f:id:ysobj:20140607124441p:plain

テーブルの行オブジェクト(org.h2.result.Rowの配列)と、そのbyte表現(org.h2.store.Data)を保持しています。データファイルは、ヘッダ部分と、キー/オフセット値部分、実際の行データ部分に分かれます。

キーは内部的なキー(前述のとおり、プライマリキーと同じこともあります)で、オフセット値はそのキーの行データがbyte表現の何バイト目から始まるかを示します。キーとオフセットはバイト列を前から使っていき、実際の行データは後ろから使っていきます。これは、PostgreSQLのテーブルのページレイアウトと同じ方式です(この方式、元があるのでしょうか)

org.h2.index.PageDataIndex

PageDataからデータを取得するためのIndexです。これはPageDataNode/PageDataLeafと異なり、データファイルには保存されず、org.h2.store.Pageの子孫でもありません。