もうすぐリリースされる parquet-1.11.0 には、ColumnIndex という機能が追加されています。
そこで、この ColumnIndex について調べてみました。
これまでの問題
Parquet にはセグメント毎に Statistics として (max, min) の値を持っています。これらの値を持つセグメントは以下の2つです。
- ColumnMetaData の ColumnChunks
- Page Header
1.は Row Group 内の各 Column の Statistics で、Parquet ファイルの Footer にあるメタデータで保持しています。2.は ColumnChunks をさらに分割したページ単位の Statistics で、各ページヘッダで保持しています。
これまでは、2.の Statistics を read する為に、Column の全てのページにアクセスする必要がありました。したがって、ほとんどのデータが Disk から read されることになります。
Goal
ページ毎に持っている Statistics を Row Group の メタデータに格納したものが ColumnIndex です。Filter を使った Selective な Scan を実行する場合、各ページの Statistics の代わりにこの ColumnIndex を read することにより、不要なページを read することを回避できます。
効果
この ColumnIndex によってどのくらいパフォーマンスが改善されるか調べてみたのですが、公式な結果は見つけられませでした。しかし、なぜか StackOverflow にテスト結果がアップされています。
https://stackoverflow.com/a/40714337/1352781
計測
parquet-mr が提供している API を使って、Parquet ファイル内にあるデータをフィルタして取り出すコードを書いてみました。
https://github.com/masayuki038/parquet-column-index-benchmark
現在の parquet-mr の master branch のコードで、ColumnIndex を使うケース (Bench.filteringScanWithColumnIndex
) と使わないケース (Bench.filteringScanWithoutColumnIndex
) を実行すると、以下のような結果になります。(Score
は小さいほど良い性能を表します)
1Benchmark Mode Cnt Score Error Units
2Bench.filteringScanWithColumnIndex avgt 20 88.439 ± 3.803 ms/op
3Bench.filteringScanWithoutColumnIndex avgt 20 6591.767 ± 51.250 ms/op
ColumnIndex を使った場合は 88.439 なのに対して、ColumnIndex を使わない場合は 6591.767 になっています。この結果から、ColumnIndex を使うことでかなり性能が向上することが分かります。
比較対象として、ColumnIndex を実装していない parquet-1.10.1 (V10) を使って同様の計測を実行してみました。
1Benchmark Mode Cnt Score Error Units
2Bench.filteringScanV10 avgt 20 6586.017 ± 51.117 ms/op
V10 では 6586.017 とかなり遅い結果となりました。V10 までの実装では、ページの Statistics を使って不要なページ read を抑える実装になっているのではないかと思ったのですが、以下のページを見るとそうではないようです。
- parquet-mr filter pushdown - Apache Mail Archives
- Does Presto’s parquet reader use page-level statistics to selectively decompress pages? - Google グループ
その結果、Bench.filteringScanWithoutColumnIndex
と Bench.filteringScanV10
の結果がほぼ同じになっています。
Conclusion
parquet-1.11.0 の新機能である ColumnIndex を使うことにより、Parquet ファイルを Filtering しながら Scan する処理が、従来よりも速くなる可能性があります。ColumnChunks 内に多数のページを持つような場合、より効果が出るはずです。
今回行った計測の結果ではかなり性能が出るように見えますが、これは parquet-mr の API がこれまでページの Statistics を見ていなかったのが大きな要因です。独自にページの Statistics を見て Filtering しているような実装では、今回のような極端な差は出ないと思います。それでも、ページの代わりに ColumnIndex の Statistics を read することによって、性能が向上する可能性があります。(本当はそれを確認したかった…)
また、V10 までの実装でも ColumnChunks の Statistics を見て Row Group 単位で Skip できるので、Parquet ファイルの ColumnChunks 内のページ数が多くない場合は効果が出にくくなります。
そもそも read する際に Filter していない場合は ColumnIndex があっても変わりません。その場合は、read する時に ColumnIndex を見ない方が良いでしょう。