データウェアハウスシリーズ - Kylin 概要#
Kylin の特徴#
Kylin の主な特徴には、SQL インターフェースのサポート、超大規模データセットのサポート、サブ秒応答、スケーラビリティ、高スループット、BI ツールの統合などがあります。
標準SQLインターフェース
:Kylin は標準の SQL を対外サービスのインターフェースとして使用しています。超大データセットのサポート
:Kylin は大データのサポート能力が現在のすべての技術の中で最も先進的です。2015 年には eBay の生産環境で 100 億レコードの秒単位のクエリをサポートし、その後、モバイルアプリケーションのシナリオで 1000 億レコードの秒単位のクエリの事例もあります。サブ秒応答
:Kylin は優れたクエリ応答速度を持ち、これは事前計算によるもので、多くの複雑な計算(結合、集約など)はオフラインの事前計算プロセスで既に完了しており、クエリ時に必要な計算量を大幅に削減し、応答速度を向上させています。スケーラビリティと高スループット
:単一ノードの Kylin は毎秒 70 のクエリを実現でき、Kylin のクラスターを構築することもできます。BIツールの統合
: Kylin は既存の BI ツールと統合でき、具体的には以下の内容が含まれます。- ODBC:Tableau、Excel、PowerBI などのツールと統合
- JDBC:Saiku、BIRT などの Java ツールと統合
- RestAPI:JavaScript、Web ページと統合
Kylin の基本アーキテクチャ#
REST サーバー
REST サーバーは、Kylin プラットフォーム向けのアプリケーション開発のためのエントリーポイントであり、クエリの提供、結果の取得、キューブ構築タスクのトリガー、メタデータの取得、ユーザー権限の取得などを実現します。また、Restful インターフェースを通じて SQL クエリを実行できます。
クエリエンジン(Query Engine)
キューブが準備完了すると、クエリエンジンはユーザーのクエリを取得し解析できます。その後、システム内の他のコンポーネントと相互作用し、ユーザーに対応する結果を返します。
ルーティング
解析された SQL から生成された実行計画をキューブキャッシュのクエリに変換する役割を担います。キューブは事前計算により HBase にキャッシュされており、この部分のクエリはミリ秒単位で完了できます。
メタデータ管理ツール(Metadata)
Kylin はメタデータ駆動型のアプリケーションです。メタデータ管理ツールは、Kylin に保存されているすべてのメタデータを管理するための重要なコンポーネントであり、最も重要なキューブメタデータを含みます。他のすべてのコンポーネントの正常な動作は、メタデータ管理ツールに基づいています。Kylin のメタデータは HBase に保存されています。
タスクエンジン(Cube Build Engine)
このエンジンは、すべてのオフラインタスクを処理することを目的として設計されており、シェルスクリプト、Java API、Map Reduce タスクなどが含まれます。タスクエンジンは Kylin 内のすべてのタスクを管理および調整し、各タスクが確実に実行され、発生した障害を解決できるようにします。
Kylin の動作原理#
Apache Kylin の動作原理は本質的にMOLAP(Multidimensional On-Line Analysis Processing)Cube
、すなわち多次元立方体分析です。詳細な概念は、データウェアハウスシリーズ - 基本概念整理を参照してください。
コアアルゴリズム#
データモデルに対してキューブの事前計算を行い、計算結果を利用してクエリを加速します:
-
データモデルを指定し、次元とメトリックを定義します;
-
キューブを事前計算し、すべてのキューブイドを計算して物化ビューとして保存します;
事前計算プロセスは、Kylin が Hive から生データを読み取り、選択した次元に基づいて計算し、結果セットを HBase に保存します。デフォルトの計算エンジンは MapReduce であり、Spark を計算エンジンとして選択することもできます。1 回のビルドの結果を、私たちは 1 つのセグメントと呼びます。構築プロセスでは、複数のキューブイドの作成が関与し、具体的な作成プロセスは kylin.cube.algorithm パラメータによって決定されます。パラメータ値は auto、layer、inmem から選択でき、デフォルト値は auto です。つまり、Kylin はデータを収集して動的にアルゴリズム(layer または inmem)を選択します。ユーザーが Kylin と自分のデータ、クラスターについてよく理解している場合は、好みのアルゴリズムを直接設定できます。 -
クエリを実行し、キューブイドを読み取り、実行し、クエリ結果を生成します。
layer(逐層構築)アルゴリズム#
N 次元のキューブは、1 つの N 次元サブキューブ、N 個の (N-1) 次元サブキューブ、N*(N-1)/2 個の (N-2) 次元サブキューブ、......、N 個の 1 次元サブキューブと 1 つの 0 次元サブキューブで構成され、合計で 2^N 個のサブキューブが構成されます。逐層アルゴリズムでは、次元数に応じて層ごとに減少して計算します。各層の計算(最初の層を除く)は、前の層の結果に基づいて計算されます。たとえば、[Group by A, B] の結果は、[Group by A, B, C] の結果に基づいて C を除去して集約することで得られます。このようにして、重複計算を減らすことができます。0 次元キューブイドが計算されると、全体のキューブの計算も完了します。
注意:
各計算のラウンドは 1 つの MapReduce タスクであり、直列に実行されます。N 次元のキューブには、少なくとも N 回の MapReduce ジョブが必要です。
アルゴリズムの利点:
- このアルゴリズムは MapReduce の能力を十分に活用し、中間の複雑なソートやシャッフル作業を処理するため、アルゴリズムコードは明確でシンプルであり、保守が容易です。
- Hadoop の成熟により、このアルゴリズムはクラスターに対する要求が低く、安定して実行されます。Kylin の内部メンテナンスの過程で、これらのステップでエラーが発生することはほとんどありません。Hadoop クラスターが比較的忙しいときでも、タスクは完了します。
アルゴリズムの欠点:
- キューブに多くの次元がある場合、必要な MapReduce タスクもそれに応じて増加します。Hadoop のタスクスケジューリングには追加のリソースが必要であり、特にクラスターが大規模な場合、タスクを繰り返し提出することによる追加のオーバーヘッドはかなりのものになります。
- Mapper が事前集約を行わないため、このアルゴリズムは Hadoop MapReduce から出力されるデータが多くなります。Combiner を使用して Mapper から Reducer へのデータ転送を減らしていますが、すべてのデータは Hadoop MapReduce を通じてソートおよび組み合わされる必要があり、無形のうちにクラスターの負担を増加させます。
- HDFS の読み書き操作が多くなります:各層の計算の出力は次の層の計算の入力として使用されるため、これらの Key-Value は HDFS に書き込む必要があります。すべての計算が完了した後、Kylin はこれらのファイルを HBase の HFile 形式に変換するために追加のタスクを実行し、HBase にインポートする必要があります。
全体的に見て、このアルゴリズムの効率は低く、特にキューブの次元数が大きい場合に顕著です。
inmem(高速構築)アルゴリズム#
「逐段」(By Segment)または「逐塊」(By Split)アルゴリズムとも呼ばれ、1.5.x
から導入されたこのアルゴリズムは、Mapper 側で計算を先に行い、大部分の集約を完了させ、その後集約された結果を Reducer に渡すことでネットワークボトルネックの圧力を軽減します。このアルゴリズムの主な考え方は:
- Mapper に割り当てられたデータブロックを計算して、完全な小さなキューブセグメント(すべてのキューブイドを含む)を生成します;
- 各 Mapper は計算が完了したキューブセグメントを Reducer に出力してマージし、大きなキューブ、すなわち最終結果を生成します;
以下の図はこのプロセスを説明しています。
layer(逐層構築)アルゴリズムと比較して、高速アルゴリズムには主に 2 つの違いがあります:
- Mapper はメモリを利用して事前集約を行い、すべての組み合わせを計算します。Mapper が出力する各 Key は異なるため、Hadoop MapReduce への出力データ量が減少し、Combiner はもはや必要ありません;
- 1 回の MapReduce で全ての層の計算が完了し、Hadoop タスクの調整が減少します。
Kylin キューブの構築プロセス#
以下の図は、キューブを構築するジョブプロセスを示しています:
いくつかの重要なプロセス分析#
Hive テーブルから Base Cuboid を生成#
実際のキューブ構築プロセスでは、まずキューブの Hive ファクトテーブルとディメンションテーブルに基づいて大きなワイドテーブルを生成し、その後、大ワイドテーブルの列の基数を計算し、ディメンション辞書を構築し、キューブのサイズを推定し、キューブに対応する HBase テーブルを構築し、base cuboid を計算します。
base cuboid の計算は MapReduce ジョブであり、その入力は上記の Hive 大ワイドテーブルで、出力の key はさまざまな次元の組み合わせ、value は Hive 大ワイドテーブルの指標の値です。
コアソースコード: org.apache.kylin.engine.mr.steps.BaseCuboidMapperBase
:
// map段階でkey-valueを生成するコード
protected void outputKV(String[] flatRow, Context context) throws IOException, InterruptedException {
byte[] rowKey = baseCuboidBuilder.buildKey(flatRow);
outputKey.set(rowKey, 0, rowKey.length);
ByteBuffer valueBuf = baseCuboidBuilder.buildValue(flatRow);
outputValue.set(valueBuf.array(), 0, valueBuf.position());
context.write(outputKey, outputValue);
}
HBase Cuboid から逐層計算 Cuboid#
コアソースコード: org.apache.kylin.engine.mr.steps.CuboidReducer
@Override
public void doReduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
aggs.reset();
for (Text value : values) {
if (vcounter++ % BatchConstants.NORMAL_RECORD_LOG_THRESHOLD == 0) {
logger.info("Handling value with ordinal (This is not KV number!): " + vcounter);
}
codec.decode(ByteBuffer.wrap(value.getBytes(), 0, value.getLength()), input);
aggs.aggregate(input, needAggrMeasures);
}
aggs.collectStates(result);
ByteBuffer valueBuf = codec.encode(result);
outputValue.set(valueBuf.array(), 0, valueBuf.position());
context.write(key, outputValue);
}
Cuboid を HBase の HFile に変換#
参考: Hive データの bulkload を HBase にインポート
Kylin のデプロイおよび基本使用#
公式リンクを参照してください: Apache Kylin
補足:
実践の過程で、私たちは Nginx + Kylin クラスターソリューションを採用しました(参考:《Kylin のクラスター展開モードの展開》)。しかし、リソースが不足しているため、Kylin クラスターと Hadoop クラスターが同じサーバー上にあり、特定の時点でリソースが不足して Kylin プロセスがクラッシュしたり、HBase がダウンする問題が頻繁に発生します。したがって、独立した Kylin クラスターをデプロイし、監視メカニズムを構築することをお勧めします。
Kylin の最適化#
Kylin の核心はキューブにあり、良いキューブを設計することは Kylin のパフォーマンスにとって非常に重要です。キューブの膨張率と構築時間に影響を与える重要な要因は以下の通りです:
- キューブ内の次元数が多く、適切なキューブイドの剪定最適化が行われていないため、キューブイドの数が非常に多くなっています;
- キューブ内に高基数の次元が存在し、そのためこの種の次元を含む各キューブイドが占めるスペースが非常に大きく、これらのキューブイドの累積が全体のキューブのサイズを増加させています;
- スペースを占有するメトリックが存在し、たとえば Count Distinct のようなもので、したがってキューブイドの各行に大きなレジスタを保存する必要があります。最悪の場合、キューブイドの各行が数十 KB になることがあり、全体のキューブのサイズが増加します。
最適化の考え方#
次元の最適化#
次元最適化手段
- 集約グループ
- 派生次元
- 強制次元
- 階層次元
- 結合次元
- 拡張列
詳細は、Apache Kylin 最適化ガイドを参照してください。
同時実行粒度の最適化#
セグメント内の特定のキューブイドのサイズが一定の閾値を超えると、システムはそのキューブイドのデータを複数のパーティションに分割し、キューブデータの読み取りを並行化して、キューブのクエリ速度を最適化します。
構築エンジンは、セグメントの推定サイズとパラメータ“kylin.hbase.region.cut”
の設定に基づいて、ストレージエンジン内でセグメントを保存するために必要なパーティションの数を決定します。ストレージエンジンが HBase の場合、パーティションの数は HBase 内のリージョンの数に対応します。kylin.hbase.region.cut
のデフォルト値は 5.0 で、単位は GB です。つまり、サイズが 50GB と推定されるセグメントには 10 個のパーティションが割り当てられます。ユーザーは、kylin.hbase.region.count.min
(デフォルトは 1)およびkylin.hbase.region.count.max
(デフォルトは 500)の 2 つの設定を通じて、各セグメントが最小または最大でいくつのパーティションに分割されるかを決定できます。
各キューブの同時実行粒度の制御は異なるため、キューブを構築する際には、各キューブに合わせて同時実行粒度の制御パラメータをカスタマイズすることをお勧めします。
参考リンク#
- Apache Kylin 深入 Cube とクエリ最適化
- Apache Kylin ジョブ生成とスケジューリングの詳細
- Apache Kylin キューブ構築原理
- Apache Kylin 最適化ガイド
- 【ケースシェア】Apache Kylin の美団点评での応用
おすすめのプログラミング小夢 | 康凯森の技術ブログ、ブロガーは Apache Kylin などの複数のオープンソースプロジェクトのコミッターであり、ブログには OLAP オフライン大データに関する多くの優れた記事があります。