PostgreSQL 9.5 リリース : BRIN インデックスが気になる

こんにちは。松本です。

PostgreSQL 9.5 がリリースされましたね!

このバージョンアップによりいくつかの新機能が追加されましたが、中でも私が注目したいのは、アナウンスでビッグデータ向け新機能として紹介されている BRIN インデックスです。

BRIN Indexing: This new type of index supports creating tiny, but effective indexes for very large, "naturally ordered" tables. For example, tables containing logging data with billions of rows could be indexed and searched in 5% of the time required by standard BTree indexes.

"BRIN" は "Block Range Index" の略です。

PostgreSQL ではテーブルデータをブロックと呼ばれる単位で格納し、インデックスはレコードがどのブロックのどの場所に格納されているかを情報として持っています。BRIN インデックスでは、連続して並ぶ複数のブロックをまとめて扱い、「ブロック範囲」ごとにその範囲に含まれるレコードのカラム(式)最大値と最小値を保持します。この仕組みにより、数十億行レベルの巨大なデータへのアクセスを高速に行うことを可能にしています。

ということで、実際に BRIN を試してみる

まずはテーブル作成とレコード追加。

約 3,000 万行ほどのレコードが登録されました。

この状態で SELECT するとこうなります。

次に BRIN インデックスを作ります。

PostgreSQL のブロックサイズのデフォルト値は 8KB で、これを変更するには PostgreSQL をソースコードからコンパイルする (--with-blocksize オプション) 必要がありますが、BRIN のブロック範囲のサイズはインデックス作成時の pages_per_range パラメーターで簡単に設定できます。

ブロック範囲サイズが小さいほどインデックスされる情報が細やかになり、インデックススキャン中に、より多くのブロックがスキップされますが、インデックス自体のサイズは大きくなります。

今回はともにデフォルト設定のままとします(ブロックサイズ 8KB、ブロック範囲サイズ 128)。

ブロックサイズが 8KB になっていることがわかります。

ここで BRIN インデックスがどのように作成されたか、contrib の pageinspect を使ってみてみます。

まずは、pageinspect のインストール。

インデックス情報を見ます。

128 ブロックずつ、サマリ情報が作成されています。

さて、SELECT はどうなったかな?

BRIN インデックスが効いているのが見て取れます。

ブロック範囲サイズを変えての比較

BRIN インデックスについて、ドキュメントには次のように書かれています。

BRIN indexes can satisfy queries via regular bitmap index scans, and will return all tuples in all pages within each range if the summary info stored by the index is consistent with the query conditions. The query executor is in charge of rechecking these tuples and discarding those that do not match the query conditions — in other words, these indexes are lossy. Because a BRIN index is very small, scanning the index adds little overhead compared to a sequential scan, but may avoid scanning large parts of the table that are known not to contain matching tuples.

(snip)

The size of the block range is determined at index creation time by the pages_per_range storage parameter. The number of index entries will be equal to the size of the relation in pages divided by the selected value for pages_per_range. Therefore, the smaller the number, the larger the index becomes (because of the need to store more index entries), but at the same time the summary data stored can be more precise and more data blocks can be skipped during an index scan.

ここから、BRIN インデックスは、「クエリの対象となるブロック範囲に含まれるレコードすべてを返し、クエリ実行部がその中から条件に一致しないレコードを除外する(recheck)」という点と、「ブロック範囲サイズを小さく設定するとインデックスサイズが大きくなる」という点について見てみます。

pages_per_range パラメーターを 64, 128, 256 とした場合それぞれのインデックスの概要と EXPLAIN ANALYZE 結果です。

pages_per_range = 64

pages_per_range = 128

pages_per_range = 256

インデックスサイズを比較して見ると、128KB, 80KB, 64KB というように、ブロック範囲サイズが小さいほど大きくなっていることがわかります。

recheck によって除外されたレコード数は 11,839 件、23,679 件、47,359 件となり、ブロック範囲サイズが小さいほど少なく、recheck 処理の効率が向上することがわかります。ブロック範囲サイズが 128 のケースを例にとると、インデックススキャンによって選択されたブロック数は 128 であり、このサンプルでは 1 ブロック範囲(128 ブロック)辺り 23,680 件のレコードを含むので、ここから除外された 23,679 件を引くと該当するレコードは 1 件、という結果になります。

ここで、BRIN インデックスのコストについて見てみると 52.01, 28.01, 20.01 となり、ブロック範囲サイズが小さいほど大きくなっていますが、このオーバーヘッドは多くの場合、無視できるほど小さいと考えられます。

EXPLAIN ANALYZE 結果として出力されている実行時間に関する数値は、様々な要因によって影響を受けるものなので、ここでは参考程度に見てください。

最後に

今回、お試し環境として AWS の EC2(Amazon Linux) t2.micro を使いました。こういう時、何の気兼ねもなく、すぐに環境を用意できるのは本当に便利ですね。

因みに、この記事を書くにあたり上記環境に PostgreSQL 9.5 をソースコードからインストールしましたが、Amazon Linux でコンパイル作業をする場合は Development Tools をインストールしておくと便利です。

Comments are closed, but you can leave a trackback: Trackback URL.