Prestoのパフォーマンス


## きっかけ

[アドテクスキルアップゼミ　カラムナーデータベース検証まとめ](http://ameblo.jp/principia-ca/entry-11933011766.html)という記事が公開されたのですが，Presto/Impalaの結果があまりにも散々で，これはさすがに何かおかしいんじゃないかという話になってました．
今だとすでに記事に注釈が入ってますが，Presto/Impalaは生のテキストファイルを対象にしていたのが原因でした．なので，その辺について少し書き，実際Prestoはどんなもんなのかというのを簡単に示します．

## 列指向ファイルフォーマット

Presto/Impalaが生のテキストファイルだったのに対し，他のクエリエンジンは違うフォーマットでデータを保存していて，これがかなり結果に響いてます．Redshift，BigQuery，Treasure Dataなど，データ解析系のサービスは皆列指向フォーマットを採用していて，データインポート時に勝手に変換が行われます．列指向フォーマットは，一般的なRDBMSが行指向で1レコードずつ保存するのとは違い，レコードの列をまとめて保存するようにしたものです．列指向フォーマットに関しては，Redshiftにドキュメントがあります．

[列指向ストレージ](http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_columnar_storage_disk_mem_mgmnt.html)

解析系のクエリでは全部のデータをスキャンするのは希で，特定の列だけを抜き出すクエリが一般的です．上記の比較記事にログの構造は書かれてませんが，Presto/Impalaが毎回1.5TB/10TBを全スキャンしていたのに対して，他のクエリエンジンは特定の列だけを読んでいたので，あれだけの差が出ていたというわけです(例えば最初のクエリはidとstrしかない)．

これを解決するには，Presto/Impalaでも列指向フォーマットで保存すればよくて，今だと[Parquet](http://parquet.incubator.apache.org/)と[ORCファイル](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC)が2強，次点でRCファイルという感じです．Prestoはすべて使えますし([NetflixはParquet](http://techblog.netflix.com/2014/10/using-presto-in-our-big-data-platform.html)．リンク先にパフォーマンス比較有り)，ImpalaはParquetもしくはRCファイルが多いようです．

## Presto with 列指向フォーマット

実際はどんなもんなのかということで，いくつかのクエリをPrestoで走らせて見ました．といっても俺自身はPrestoクラスタを持っていないので，使ったのはTreasure DataのTQA(バックエンドはPresto)になります．
構成的にはc3.8xlargeが複数台ワーカーとして立っていて，マルチテナントでそれらをフルに使ったり使わなかったりという感じの構成です．これのために専用のワーカー群を立てても良かったのですが，今回のクエリ群はそんなに重くないですし，c3.4xlargeとc3.8xlargeの差はほとんど出ないと思っています(逆に台数が多い分，分散具合でc3.4xlarge x 9の方が有利かも？)．

### 対象のテーブル

今までの様々なアクセスログを突っ込んで，圧縮後1.6TBにまでなっているテーブルです．非圧縮だと16+TBくらいにはなると思います(Treasure Dataのテーブルサイズ表記は圧縮後のサイズ)．

![td_test_table.png](https://qiita-image-store.s3.amazonaws.com/0/566/2c0ceb3c-fda9-808f-9cb7-135542d88df4.png "td_test_table.png")

これやった後に気づきましたが，上記の比較記事はそもそも非圧縮で1.5TB/10TBだったので，正確な比較にはなってません．Prestoがそれなりに高速に動くことが分かれば〜という感じです．

### 1.5TBのクエリ1

例: `SELECT DISTINCT id FROM table WHERE str LIKE 'string%'`

33秒．多分LIKEで少しCPUを使ったのかなという感じです．`WHERE`のプッシュダウンで各ワーカーでかなりのデータがスキップされます．

![presto_query_result.png](https://qiita-image-store.s3.amazonaws.com/0/566/69c24238-a223-92c7-66b2-ec1e2fe5fabf.png "presto_query_result.png")


### 10TBのクエリ1

例: `SELECT id,count(*) FROM table GROUP BY idx`

14秒．単なるGROUP BYで各ワーカーで処理が分散されるので，こんなもんかなという感じですね．

![td_presto_query2.png](https://qiita-image-store.s3.amazonaws.com/0/566/566ffa7c-b20e-bd12-ecc3-589680bafeef.png "td_presto_query2.png")


### 10TBのクエリ4

例: `SELECT min(id),count(*) FROM table WHERE column = 'string' GROUP BY id ORDER BY count DESC`

18秒．こちらもプッシュダウン and 分散して各ワーカーで処理が出来るので，こんなもんかなという感じですね．

![td_presto_query5.png](https://qiita-image-store.s3.amazonaws.com/0/566/fd8abbcc-3e08-4923-9f2a-ac5a5ca4dec2.png "td_presto_query5.png")


### 10TBのクエリ2と3

やりたかったのですが，今もっているテーブルとJOIN出来る他のテーブルがないのでやってません．JOINがあるもののそれぞれは分散して処理出来るので，そんなに大きくパフォーマンスが落ちることはないんじゃないかなと思います(実際Redshiftとかはクエリ4より高速)．

### 注意事項

今回はTreasure DataのPrestoなので，列指向ファイルフォーマットはParquetやORCファイルとは違うPlazmaという独自ストレージのものになっています．なので，通常のPrestoをParquet/ORCファイル on HDFSのような環境で走らせたのとは，パフォーマンス傾向に違いがあることに注意してください．
また，Prestoのバージョン・ログの構造・実クエリなどが元記事で公開されてなかったので，実際読み込んでいるデータサイズなどに違いがあり，上で一度書きましたが正確な比較にはなってません．こういう感じのパフォーマンスも出るよという目安にしておいてください．

## まとめ

Treasure Dataで圧縮後1.6TBのテーブルを用いて，比較記事にあったそれぞれのクエリを模倣してPrestoで処理した結果は以下のようになりました．

| 1.5TBのクエリ1 | 10TBのクエリ1 | 10TBのクエリ2 | 10TBのクエリ3 |10TBのクエリ4 |
|--:|--:|--:|--:|--:|
| 33秒 | 14秒 | skip | skip | 18秒 |

ワーカー数が少ない中，中々良い結果なのではないかなと思います(個人的には，比較記事のRedshiftが遅すぎるような気はしています)．特に今回のは読む列が少ないのと，countなどの集計クエリがメインでストリーミング的に処理をするエンジンだとオンメモリでかなり効率的に動きます．列指向フォーマットであれば，他に大きいサイズのデータがあってもそれをスキップ出来ますし，圧縮なども聞いてIO効率があがるため，かなりパフォーマンスを稼げます．
Presto/Impalaを使う時にはこの辺に気をつけましょう．あと勿論，並列度をあげるために適度にワーカーを用意することも大事です．