LiulishuoデータアクセスにおけるAlibabaCloud EMR DeltaLakeのアーキテクチャと実践

バックグラウンド

Liulishuo氏によると、現在のオフラインコンピューティングタスクのほとんどはビジネスDBからのものであり、ビジネスDBデータアクセスの正確性、安定性、適時性によって、ダウンストリームのオフラインコンピューティングパイプライン全体の正確性と適時性が決まります。同時に、まだいくつかのビジネス要件があります。DB内のデータとハイブ内のデータのほぼリアルタイムの共同クエリを実行する必要があります。

アリババクラウドEMRデルタレイクが導入される前に、DataXをカプセル化してビジネスDBデータへのアクセスを完了しました。マスタースレーブアーキテクチャを使用して、マスターは毎日実行されるDataXタスクのメタデータ情報を維持し、ワーカーノードは継続的にプリエンプトします。その日のすべてのDataXタスクが実行されるまで、メソッドでinitおよびrestableステータスのDataXタスクを取得して実行します。

アーキテクチャ図はおおまかに次のとおりです。

ワーカー処理のプロセスは次のとおりです。

ほぼリアルタイムの要件については、スレーブライブラリを直接開き、スレーブライブラリに接続するようにprestoコネクタを構成して、ビジネスBDのデータとハイブのデータのほぼリアルタイムの共同クエリ要件を実現します。

このアーキテクチャスキームの利点は、シンプルで実装が簡単です。ただし、データの量が増えると、欠点が徐々に明らかになります。
パフォーマンスのボトルネック:ビジネスが成長するにつれて、SELECTを介してデータにアクセスするこの方法のパフォーマンスは、DBのパフォーマンスのボトルネックの影響を受けて、ますます悪化します。ワーカーノードを追加しても軽減できません。

大規模なテーブルはライブラリからのみプルできるため、データアクセスコストはますます高くなります。

ビジネスはほぼリアルタイムのクエリ要件を満たすことができず、ほぼリアルタイムのクエリはデータベースからのクエリのみであるため、アクセスのコストがさらに増加し​​ます。

これらの問題を解決するために、CDCリアルタイムアクセスソリューションに注目しました。

技術スキームの選択

CDCリアルタイムアクセスソリューションの場合、業界には主に次のものがあります。CDC+マージソリューション、CDC + Hudi、CDC + Delta Lake、およびCDC + Iceberg。その中で、CDC + Mergeソリューションは、データレイクソリューションが登場する前に実装されていました。このソリューションは、DBデータベースのコストを節約できますが、リアルタイムクエリやその他の機能に近いビジネスのニーズを満たすことができないため、最初にパスが削除されました。選考当初、アイスバーグは十分に成熟しておらず、業界にも参考事例がなかったため合格となり、最終的にはCDC +フディとCDC +デルタレイクのどちらかを選びました。
モデルを選択するとき、HudiとDelta Lakeの機能は類似しているため、主に次のソリューションを考慮します:安定性、小さなファイルのマージ、SQLサポート、クラウドベンダーのサポート、言語のサポートいくつかの考慮事項を待ちます。

上記の指標に基づいて、データプラットフォーム全体がAlibaba Cloud EMRに基づいているため、Delta Lakeを選択すると、多くの適応開発作業を節約できるため、最終的にCDC + DeltaLakeソリューションを選択しました。
全体的な構造

全体的なアーキテクチャ図

全体的なアーキテクチャを上の図に示します。アクセスするデータは、ストック履歴データと新しいデータの2つの部分に分けられます。ストック履歴データは、DataXを使用してMySQLからエクスポートされ、OSSに保存され、新しいデータは、Binlogを使用してDeltaLakeテーブルに収集および保存されます。毎日早朝にETLタスクを実行する前に、履歴データと新しいデータに対してマージ操作を実行します。ETLタスクはマージ後のデータを使用します。

デルタレイクのデータアクセス

Binlogのリアルタイム収集に関しては、MySQLからBinlogをリアルタイムで取得し、適切な分析を完了する責任があるオープンソースのDebeziumを使用します。各テーブルはトピックに対応し、サブデータベースとサブテーブルは1つのトピックにマージされ、アップストリームで使用するためにKafkaに配布されます。BinlogデータがKafkaに接続されたら、対応するKafkaトピックを指すKafkaSourceテーブルを作成する必要があります。テーブルの形式は次のとおりです。

CREATE TABLE kafka_{db_name}_{table_name} (key BINARY, value BINARY, topic STRING, partition INT, offset BIGINT, timestamp TIMESTAMP, timestampType INT)
USING kafka
OPTIONS (
kafka.sasl.mechanism 'PLAIN',
subscribe 'cdc-{db_name}-{table_name}',
serialization.format '1',
kafka.sasl.jaas.config '*****(redacted)',
kafka.bootstrap.servers '{bootstrap-servers}',
kafka.security.protocol 'SASL_PLAINTEXT'
)

使用する主なフィールドは値とオフセットです。値の形式は次のとおりです。

{
"payload": {
"before": {
db记录变更前的schema及内容,op=c时,为null
},
"after": {
db记录变更后的schema及内容,op=d时,为null
},
"source": {
ebezium配置信息
},
"op": "c",
"ts_ms":
}
}

同時に、Delta Lakeテーブルを作成します。LocationはHDFSまたはOSSを指し、テーブル構造は次のとおりです。

CREATE TABLE IF NOT EXISTS delta.delta_{dbname}{table_name}(
{row_key_info},
ts_ms bigint,
json_record string,
operation_type string,
offset bigint
)
USING delta
LOCATION '------/delta/{db_name}.db/{table_name}'

その中で、row_key_infoはDelta Lakeテーブルの一意のインデックスフィールドです。単一のデータベースと単一のテーブルの場合、row_key_infoはmysqlテーブルのプライマリキーフィールドです。例:id long。データベースとインスタンスのサブデータベースサブテーブルの場合、row_key_infoはサブテーブルです。データベースサブテーブルフィールドは、単一テーブルのプライマリキーフィールドで構成されます。たとえば、user_idはサブテーブルフィールド、idは各テーブルのプライマリキーであり、対応するrow_key_infoはidlongおよびuser_idlongです。

StreamingSQLは、Kafkaのデータを処理します。主に、オフセット、値フィールド、およびKafkaソーステーブルの値フィールド(op、ts_ms、ペイロードの前後フィールドなど)のCDC情報を抽出します。

StreamingSQLでは、5分のミニバッチを使用します。ミニバッチが小さすぎると、多数の小さなファイルが生成され、処理速度が遅くなり、読み取りパフォーマンスにも影響するためです。大きすぎると、ほぼリアルタイムのクエリに対応できません。請求。Delta Lakeテーブルの場合、afterフィールドまたはbeforeフィールドを解析しません。主な理由は、ビジネステーブルのスキーマが頻繁に変更されるためです。ビジネステーブルのスキーマが変更されると、データを修復する必要があり、比較的コストがかかります。

StreamingSQLの処理中に、op = 'c'を使用してデータを直接挿入し、json_recordはafterフィールドを取ります。op = 'u'またはop = 'd'のデータの場合、データがDelta Lakeテーブルに存在しない場合は、挿入操作を実行します。存在する場合は、更新操作を実行します。json_record、op = 'd'、json_recordの割り当て値は前にかかります。フィールド、op = 'u'、jsonrecordはafterフィールドを取ります。削除されたデータが在庫履歴テーブルにある可能性があることを主に考慮して、op = 'd'フィールドを保持します。直接削除された場合、早朝のマージデータのデータは削除されません。

StreamingSQL全体の処理はおおよそ次のとおりです。

CREATE SCAN incremental{dbname}{tablename} on kafka{dbname}{table_name} USING STREAM
OPTIONS(
startingOffsets='earliest',
maxOffsetsPerTrigger='1000000',
failOnDataLoss=false
);
CREATE STREAM job
OPTIONS(
checkpointLocation='------/delta/{db_name}.db/{table_name}checkpoint',
triggerIntervalMs='300000'
)
MERGE INTO delta.delta{dbname}{table_name} as target
USING (
SELECT * FROM (
SELECT ts_ms, offset, operation_type, {key_column_sql}, coalesce(after_record, before_record) as after_record, row_number() OVER (PARTITION BY {key_column_partition_sql} ORDER BY ts_ms DESC, offset DESC) as rank
FROM (
SELECT ts_ms, offset, operation_type, before_record, after_record, {key_column_include_sql}
FROM ( SELECT get_json_object(string(value), '$.payload.op') as operation_type, get_json_object(string(value), '$.payload.before') as before_record,
get_json_object(string(value), '$.payload.after') as after_record, get_json_object(string(value), '$.payload.ts_ms') as tsms,
offset
FROM incremental{dbname}{table_name}
) binlog
) binlog_wo_init ) binlog_rank where rank = 1) as source
ON {key_column_condition_sql}
WHEN MATCHED AND (source.operation_type = 'u' or source.operation_type='d') THEN
UPDATE SET {set_key_column_sql}, ts_ms=source.ts_ms, json_record=source.after_record, operation_type=source.operation_type, offset=source.offset
WHEN NOT MATCHED AND (source.operation_type='c' or source.operation_type='u' or source.operation_type='d') THEN
INSERT ({inser_key_column_sql}, ts_ms, json_record, operation_type, offset) values ({insert_key_column_value_sql}, source.ts_ms, source.after_record, source.operation_type, source.offset);

StreamingSQLを実行すると、次の形式のデータが生成されます。

Part-xxxx.snappy.parquetは、DeltaLakeテーブルのデータファイルを保存し、_deltalogディレクトリは、次のようなDeltaLakeテーブルのメタデータを保存します。

ここで、xxxxxxxxはバージョン情報を表し、有効な寄木細工のファイル情報はxxxxxxxx.jsonファイルに保存されます。ここで、追加タイプは有効な寄木細工のファイルであり、削除は無効な寄木細工のファイルです。

Delta Lakeはタイムトラベルをサポートしていますが、CDCデータにアクセスする場合、データのロールバック戦略は必要ありません。複数のバージョンのデータが保持されている場合、ストレージに一定の影響があるため、古いバージョンを定期的に削除する必要があります。現在、データは2時間以内のバージョンデータのみを保持しています。同時に、Delta Lakeは小さなファイルを自動的にマージする機能をサポートしていないため、小さなファイルを定期的にマージする必要もあります。現在の慣行では、OPTIMIZEとVACCUMを使用して小さなファイルをマージし、期限切れのデータファイルを1時間ごとにクリーンアップします。

optimize delta{dbname}{tablename};
set spark.databricks.delta.retentionDurationCheck.enabled = false;
VACUUM delta{dbname}{table_name} RETAIN 1 HOURS;

HiveとPrestoはSparkSQLによって作成されたDeltaLakeテーブルを直接読み取ることはできませんが、監視とほぼリアルタイムのクエリ要件のために、Delta Lakeテーブルをクエリする必要があるため、HiveテーブルとPrestoテーブルのクエリも作成しました。

デルタレイクデータとインベントリデータのマージ

Delta Lakeデータの新しいデータにのみアクセスするため、DataXを介して一度に在庫履歴データをインポートします。DeltaLakeテーブルHiveを直接クエリすることはできません。したがって、データの2つの部分を毎朝マージする必要があります。 、SparkSQLとHiveを統合して使用するための新しいテーブルに書き込まれます。次のように、このモジュールのアーキテクチャは、おおよそである:
画像

毎日午前0時前に、DeltaService APIを呼び出し、Delta Lakeタスクの構成に従って、マージタスクのタスク情報、spark-sqlスクリプト、および対応するAirflowDAGファイルを自動的に生成します。
マージタスクのタスク情報には、主に次の情報が含まれます。

マージスクリプトは、主にDelta Lakeタスクの構成からmysqlテーブルのスキーマ情報を取得し、履歴Hiveテーブルを削除し、スキーマ情報に基づいてHive外部テーブルを再作成し、DeltaLakeテーブルのjson_recordフィールドから新しいスキーマを使用するために自動的に生成されます。履歴ストックデータテーブルから対応するフィールド値を取得し、union all操作を実行します。欠落している値はmysqlのデフォルト値を採用します。unionの後、row_keyに従ってグループ化され、最初の値はts_msでソートされ、operation_type = 'd'のデータが同時に取り出されます。全体は次のとおりです。

CREATE DATABASE IF NOT EXISTS {db_name} LOCATION '------/delta/{db_name}.db';
DROP TABLE IF EXISTS {db_name}.{table_name};
CREATE TABLE IF NOT EXISTS {db_name}.{table_name}(
{table_column_infos}
)
STORED AS PARQUET
LOCATION '------/delta/{db_name}.db/{table_name}/data_date=${
   
   {data_date}}';
INSERT OVERWRITE TABLE {db_name}.{table_name}
SELECT {table_columns}
FROM ( SELECT {table_columns}, _operation_type, row_number() OVER (PARTITION BY {row_keys} ORDER BY ts_ms DESC) as ranknum
FROM (
SELECT {delta_columns}, operation_type as _operation_type, tsms
FROM delta{dbname}{table_name}
UNION ALL
SELECT {hive_columns}, 'c' as _operation_type, 0 as ts_ms
FROM {db_name}.{table_name}_delta_history
) union_rank
) ranked_data
WHERE ranknum=1
AND _operation_type <> 'd'

午前0時以降、AirflowはAirflowDAGファイルに従ってマージのSparkSQLスクリプトを自動的にスケジュールして実行します。スクリプトが正常に実行されると、マージタスクのステータスが成功に更新され、AirflowのETL DAGは、マージタスクのステータスに従ってダウンストリームETLタスクを自動的にスケジュールします。

デルタレイクデータモニタリング

Delta Lakeデータの監視では、主にMySQLとDelta Lakeテーブル間、およびCDCがアクセスするKafkaTopicテーブルとDeltaLakeテーブル間で、データが遅延するかどうか、および監視データが失われるかどうかを監視するという2つの目的を果たします。

CDCがアクセスするKafkaトピックとDeltaLakeテーブル間の遅延監視:15分ごとにKafkaトピックからの各パーティションの最大オフセットに対応するMySQLのrow_keyフィールドコンテンツを取得し、監視対象のMySQLテーブルdelta_kafka_monitor_infoに配置します。 、次に、前のサイクルのrow_keyフィールドの内容をdelta_kafka_monitor_infoから取得し、Delta Lakeテーブルでクエリします。クエリできない場合は、データが遅延または失われていることを意味し、アラームが発行されます。

MySQLとDeltaLake間の監視:2つのタイプがあります。1つはプローブソリューションです。15分ごとに最大のIDがMySQLから取得されます。サブデータベースとサブテーブルの場合、1つのテーブルのみが監視され、delta_mysql_monitor_infoに格納されます。 、次に、delta_mysql_monitor_infoから前の期間の最大IDを取得し、Delta Lakeテーブルでクエリします。クエリが失敗した場合は、データが遅延または失われたことを示し、アラームが発行されます。もう1つはdirectcount(id)です。このスキームは、単一のデータベースと単一のテーブルおよびサブデータベースのサブテーブルに分割されます。メタデータは、主にmin_id、max_id、およびmysql_countの3つのフィールドを含むmysqlテーブルid_based_mysql_delta_monitor_infoに格納されます。ライブラリリストテーブルは、5分ごとにDelta Lakeテーブルからmin_idとmax_idの間のカウント値を取得し、それをmysql_countと比較します。mysql_count値よりも小さい場合は、データの損失または遅延があることを示し、アラームが発行されます。次に、mysqlからmax(id)とmax_idおよびmax(id)の間のカウント値を取得し、それをid_based_mysql_delta_monitor_infoテーブルに更新します。サブデータベースとテーブルの場合、サブデータベースとテーブルのルールに従って、各テーブルに対応するid_based_mysql_delta_monitor_info情報が生成され、監視は30分ごとに実行され、ルールは単一データベースと単一テーブルのルールと同じです。

遭遇した課題

ビジネステーブルのスキーマは頻繁に変更されます。DeltaLakeテーブルがCDCのフィールド情報を直接解析する場合、データが見つからず、時間内に修復できないと、後の期間にデータを修復するコストが高くなります。現在、フィールドを解析せず、早朝のマージまで待機します。 。

データ量が増えると、StreamingSQLタスクのパフォーマンスが低下します。現在、StreamingSQLで遅延を処理しています。多数の遅延アラームが発生した後、Delta Lakeインベントリデータは昨日のマージ後のデータに置き換えられ、Delta Lakeテーブルが削除され、チェックポイントデータが削除され、KafkaSourceテーブルデータが最初から消費されます。Delta Lakeテーブルのデータを減らして、StreamingSQLのプレッシャーを軽減します。

HiveとPrestoは、SparkSQLによって作成されたDeltaLakeテーブルを直接クエリすることはできません。現在、HiveとPrestoが使用するHiveとPrestoクエリをサポートする外部テーブルを作成していますが、これらのテーブルはSparkSQLを介してクエリできません。したがって、上位レベルのETLアプリケーションは、コードを変更せずにHiveエンジンとSparkSQLエンジンおよびPrestoエンジンを自由に切り替えることはできません。

利点

CDC + Delta Lakeを採用した後、DBスレーブライブラリのコストを節約しました。コストは80%近く節約されました。
早朝のDBデータアクセスの時間コストが大幅に削減され、特別に必要とされないすべてのDBデータアクセスを1時間以内に完了することができます。

フォローアップ計画

Delta Lakeテーブルのデータ量が増え、StreamingSQLタスクのパフォーマンスが低下しているため、フォローアップしてください。
SparkSQLによって作成されたDeltaLakeテーブルをHiveとPrestoで直接クエリできないという問題をプロモーションで解決できるかどうか。

 

元のリンク

この記事はAlibabaCloudのオリジナルのコンテンツであり、許可なく複製することはできません。

おすすめ

転載: blog.csdn.net/weixin_43970890/article/details/112860047