簡単な概要
PrestoDB の Aria プロジェクトは、2020 年に一連の実験的機能をリリースして、テーブル (Hive コネクタ経由で接続され、ORC 形式でデータを保存する) のスキャン パフォーマンスを向上させました。
この記事では、Docker ベースの PrestoDB テスト環境でこれらの新機能の基本的なテストを実行します。[1]
後で
Presto は超並列処理 (MPP) が可能な SQL 実行エンジンです。実行エンジンはデータ ストアから分離されており、プロジェクトにはクエリされたデータを Presto エンジンに提供する多数のプラグイン (コネクタ、コネクタとも呼ばれます) が含まれています。データ ストア内のデータが読み取られた後、Presto に引き渡され、データの結合や集計などのクエリ操作が実行されます。このデータ ストレージと実行の分離アーキテクチャにより、単一の Presto インスタンスで複数のデータ ソースにクエリを実行できるようになり、非常に強力なフェデレーション クエリ レイヤーが提供されます。
Presto には多くのコネクタが用意されており、コミュニティはデータ ストアにアクセスするための新しいコネクタを定期的に提供しています。
ハイブ コネクタ
Hive コネクタは、一般的に Presto の標準コネクタと見なされています。通常、これを使用して Hive メタストアに接続し、メタストアで定義されているテーブルに関するメタデータ情報を取得します。通常、データは HDFS または S3 に保存され、Metastore はファイルが保存されている場所と形式に関する情報を提供します。ORC 形式が最も一般的に使用されますが、Avro や Parquet などの他の形式もサポートされています。Hive コネクタにより、Presto エンジンは HDFS/S3 からエンジンにデータを並行してスキャンし、クエリを実行できます。ORC 形式は非常に標準的で一般的なデータ ストレージ形式であり、優れた圧縮率とパフォーマンスを提供できます。
クエリを実行するための 2 つのコア サービス
Presto には、クエリを実行するための 2 つのコア サービスがあります。クエリの解析とタスクのスケジューリングを担当する Coordinator と、クエリを並行して実行する複数の Worker です。理論的には、コーディネーターはワーカーとしても機能できますが、本番環境ではそうではありません。ここでは Presto をテストしているので、便宜上、Coordinator と Worker の両方として 1 つのノードのみを使用します。[2]
この Presto テストには単一の Docker コンテナを使用します。クリックして展開ドキュメントを表示してください。単一ノードの Presto 展開を実装する方法の例は、ドキュメントの最後にあります。
Presto がクエリを実行する方法は次のとおりです。
まず、Presto コーディネーターがクエリ ステートメントを解析して実行計画を作成します (例を以下に示します)。計画が作成されると、フェーズ (またはフラグメント) に分割されます。各フェーズは、一連の操作、つまりエンジンがクエリを実行するために使用する特定の機能を実行します。実行計画は通常、コネクタによるデータのスキャンから始まり、データのフィルタリング、部分的な集計、Presto ワーカー ノード間でのデータ交換などの一連の操作を実行して、データの結合や最終的なデータの集計などを実行します。これらのステージはすべて、Presto の並列実行ユニットである複数のスプリットに分割されます。ワーカーは、設定可能な数のシャードを並行して実行して、目的の結果を達成します。エンジン内のすべてのデータはメモリに保持されます (クラスターの容量しきい値を超えていない場合)。
Hive コネクタ (および他のすべてのコネクタ) は、Presto が並行して読み取るために、入力データセットを複数のシャードに分割する役割を果たします。最適化として、Presto エンジンは、クエリで使用される述語と選択された列をコネクタに通知します (述語プッシュダウンと呼ばれます)。
述語プッシュダウンを示すために、基本的なクエリを見てみましょう - データ テーブル内の適格な行の数を数えます。クエリの例は、ベンチマーク データセット TPC-H の lineitem データ テーブルに基づいています。TPC-H の lineitem テーブルには約 6 億行あり、それらの出荷日フィールドの値は 1992 年から 1998 年の間です。次のクエリ ステートメントは、lineitem データ テーブルの設定条件のフィルター述語であり、shipdate フィールドが 1992 であるデータ行を除外します。まず、Aria 拡張セッション属性を有効にせずに EXPLAIN コマンドを実行して、クエリ プランを確認しましょう。
presto:tpch> EXPLAIN (TYPE DISTRIBUTED) SELECT COUNT(shipdate) FROM lineitem WHERE shipdate BETWEEN DATE '1992-01-01' AND DATE '1992-12-31';
Fragment 0 [SINGLE]
Output layout: [count]
Output partitioning: SINGLE []
Stage Execution Strategy: UNGROUPED_EXECUTION
- Output[_col0] => [count:bigint]
_col0 := count
- Aggregate(FINAL) => [count:bigint]
count := ""presto.default.count""((count_4))
- LocalExchange[SINGLE] () => [count_4:bigint]
- RemoteSource[1] => [count_4:bigint]
Fragment 1 [SOURCE]
Output layout: [count_4]
Output partitioning: SINGLE []
Stage Execution Strategy: UNGROUPED_EXECUTION
- Aggregate(PARTIAL) => [count_4:bigint]
count_4 := ""presto.default.count""((shipdate))
-ScanFilter[table = TableHandle {
connectorId='hive', connectorHandle='HiveTableHandle{
schemaName=tpch, tableName=lineitem, analyzePartitionValues=Optional.empty}', layout='Optional[tpch.lineitem{
domains={
shipdate=[ [[1992-01-01, 1992-12-31]] ]}}]'}, grouped = false, filterPredicate = shipdate BETWEEN (DATE 1992-01-01) AND (DATE 1992-12-31)] => [shipdate:date]
Estimates: {
rows: 600037902 (2.79GB), cpu: 3000189510.00, memory: 0.00, network: 0.00}/{
rows: ? (?), cpu: 6000379020.00, memory: 0.00, network: 0.00}
LAYOUT: tpch.lineitem{
domains={
shipdate=[ [[1992-01-01, 1992-12-31]] ]}}
shipdate := shipdate:date:10:REGULAR
クエリ プランは、フラグメント 1 から開始してボトムアップ順に読み取られ、lineitem テーブルを並行してスキャンし、述語を使用して shipdate 列をフィルター処理し、次に各シャードで部分的な集計を実行し、その部分的な結果を次のステージのフラグメントにスワップします0 to perform the final aggregate, and then send the result to client. クエリ プランのプロセスを次の図に示します (図の下部近くの水平線は、Hive コネクタで実行されるコードと、実行されるコードを示します)。 Presto エンジンで実行されます。)
それでは、このクエリを実行してみましょう。
presto:tpch> SELECT COUNT(shipdate) FROM lineitem WHERE shipdate BETWEEN DATE '1992-01-01' AND DATE '1992-12-31';
_col0
----------
76036301
(1 row)
Query 20200609_154258_00019_ug2v4, FINISHED, 1 node
Splits: 367 total, 367 done (100.00%)
0:09 [600M rows, 928MB] [63.2M rows/s, 97.7MB/s]
lineitem テーブルには、shipdate 列の値が 1992 年である 7600 万を超える行が含まれていることがわかります。このクエリの実行には約 9 秒かかり、合計 6 億行のデータが処理されました。
それでは、セッション プロパティ pushdown_subfields_enabled と hive.pushdown_filter_enabled を有効にして、Aria 機能を有効にしましょう。クエリ プランがどのように変更されたかを見てみましょう。
presto:tpch> SET SESSION pushdown_subfields_enabled=true;
SET SESSION
presto:tpch> SET SESSION hive.pushdown_filter_enabled=true;
SET SESSION
presto:tpch> EXPLAIN (TYPE DISTRIBUTED) SELECT COUNT(shipdate) FROM lineitem WHERE shipdate BETWEEN DATE '1992-01-01' AND DATE '1992-12-31';
Fragment 0 [SINGLE]
Output layout: [count]
Output partitioning: SINGLE []
Stage Execution Strategy: UNGROUPED_EXECUTION
- Output[_col0] => [count:bigint]
_col0 := count
- Aggregate(FINAL) => [count:bigint]
count := ""presto.default.count""((count_4))
- LocalExchange[SINGLE] () => [count_4:bigint]
- RemoteSource[1] => [count_4:bigint]
Fragment 1 [SOURCE]
Output layout: [count_4]
Output partitioning: SINGLE []
Stage Execution Strategy: UNGROUPED_EXECUTION
- Aggregate(PARTIAL) => [count_4:bigint]
count_4 := ""presto.default.count""((shipdate))
- TableScan[TableHandle {
connectorId='hive', connectorHandle='HiveTableHandle{
schemaName=tpch, tableName=lineitem, analyzePartitionValues=Optional.empty}', layout='Optional[tpch.lineitem{
domains={
shipdate=[ [[1992-01-01, 1992-12-31]] ]}}]'}, grouped = false] => [shipdate:date]
Estimates: {
rows: 540034112 (2.51GB), cpu: 2700170559.00, memory: 0.00, network: 0.00}
LAYOUT: tpch.lineitem{
domains={
shipdate=[ [[1992-01-01, 1992-12-31]] ]}}
shipdate := shipdate:date:10:REGULAR
:: [[1992-01-01, 1992-12-31]]
注: クエリ プランの主な変更点は下部にあります。これは、TableScan 操作に shipdate 列が含まれていることです。コネクタは、shipdate 列で述語条件 (1992 年 1 月 1 日から 1992 年 12 月 31 日までの値) を受け取りました。次の図に示すように、この述語はコネクタにプッシュ ダウンされるため、クエリ エンジンがこのデータをフィルター処理する必要がなくなります。
もう一度クエリを実行してみましょう。
presto:tpch> SELECT COUNT(shipdate) FROM lineitem WHERE shipdate BETWEEN DATE '1992-01-01' AND DATE '1992-12-31';
_col0
----------
76036301
(1 row)
Query 20200609_154413_00023_ug2v4, FINISHED, 1 node
Splits: 367 total, 367 done (100.00%)
0:05 [76M rows, 928MB] [15.5M rows/s, 189MB/s]
クエリを実行した後、同じ結果が得られましたが、クエリ時間はほぼ半分になり、さらに重要なことに、クエリは 7,600 万行しかスキャンしませんでした。コネクタは、エンジンに述語を処理させる代わりに、shipdate 列に既に述語を適用しているため、CPU サイクルが節約され、クエリが高速化されます。クエリやデータセットによって異なる場合がありますが、Hive コネクタを介して ORC ファイルにクエリを実行している場合、このソリューションは試してみる価値があります。
記事の著者: Adam Shook 元のテキストは、2020 年 6 月 15 日に著者の個人ブログで公開されました: http://datacatessen.com
[1] Aria プロジェクトの機能の詳細
については、記事の下部を参照してください https://engineering.fb.com/2019/06/10/data-infrastructure/aria-presto/
[2 】 詳しくはこちら インストールなど詳細は記事下部のドキュメントをご確認ください https://prestodb.io/docs/current/