Kafka から ClickHouse
ClickHouse Cloud をご利用の場合は、代わりに ClickPipes の利用をお勧めします。ClickPipes は、プライベートネットワーク接続に対応しており、インジェストとクラスターリソースを個別にスケーリングできるほか、Kafka データを ClickHouse にストリーミングで取り込むための包括的な監視機能を備えています。
概要
手順
1
準備
ターゲットトピックにデータが投入されている場合は、以下の内容を調整して自分のデータセットに使用できます。あるいは、サンプルの GitHub データセットをこちらで提供しています。このデータセットは以下の例で使用しており、簡潔にするため、こちらで利用できる完全なデータセットと比べて、簡略化したスキーマと行の一部のみ (具体的には、ClickHouse リポジトリに関する GitHub イベントに限定) を使用しています。それでも、データセットとともに公開されているほとんどのクエリを実行するには十分です。
2
ClickHouse を設定する
この手順は、セキュアな Kafka に接続する場合に必要です。これらの設定は SQL DDL コマンドでは渡せないため、ClickHouse の config.xml で設定する必要があります。ここでは、SASL で保護されたインスタンスに接続することを前提としています。Confluent Cloud を利用する場合、これが最も簡単な方法です。上記のスニペットは、データベースを作成したら、そのデータベースに切り替える必要があります:
conf.d/ ディレクトリ配下の新しいファイルに配置するか、既存の設定ファイルにマージしてください。設定可能な項目については、こちらを参照してください。このチュートリアルで使用する KafkaEngine というデータベースも作成します。3
宛先テーブルを作成する
宛先テーブルを準備します。以下の例では、簡潔にするため、簡略化した GitHub のスキーマを使用しています。ここでは MergeTree テーブルエンジンを使用していますが、この例は MergeTree family のどのメンバーにも簡単に適用できます。
4
トピックを作成し、データを投入する
次に、トピックを作成します。これを行うために使用できるツールはいくつかあります。Kafka をローカルマシン上または Docker コンテナ内で実行している場合は、RPK が適しています。以下のコマンドを実行すると、5 つのパーティションを持つ Kafka を Confluent Cloud 上で実行している場合は、Confluent CLI を使用するとよいでしょう:次に、この topic にデータをいくつか投入する必要があります。これには kcat を使用します。Kafka をローカルで実行しており、authentication が無効になっている場合は、以下のようなコマンドを実行できます。または、Kafka クラスターで認証に SASL を使用している場合は、以下を使用します。このデータセットには 200,000 行が含まれているため、数秒で取り込まれるはずです。より大きなデータセットを扱いたい場合は、ClickHouse/kafka-samples GitHub リポジトリの large datasets section を参照してください。
github という名前のトピックを作成できます。5
Kafkaテーブルエンジンを作成する
以下の例では、MergeTree テーブルと同じスキーマを持つテーブルエンジンを作成します。これは厳密には必須ではなく、ターゲットテーブルにエイリアスや一時的なカラムを含めることもできます。ただし、設定は重要です。Kafka トピックから JSON を取り込むデータ型として 以下では、エンジン設定とパフォーマンスチューニングについて説明します。この時点で、テーブル
JSONEachRow を使用している点に注意してください。値 github と clickhouse は、それぞれトピック名とコンシューマグループ名を表します。実際には、topics には値のリストを指定することもできます。github_queue に対する単純な SELECT でいくつかの行を読み取れるはずです。 これによりコンシューマーオフセットが進むため、reset を行わない限り、これらの行は再度読み取れなくなる点に注意してください。制限事項と必須パラメータ stream_like_engine_allow_direct_select に注意してください。6
materialized viewを作成する
materialized view は、前に作成した 2 つのテーブルを接続し、Kafka テーブルエンジンからデータを読み取って、ターゲットの MergeTree テーブルに挿入します。さまざまなデータ変換を行えますが、ここでは単純な読み取りと挿入を行います。* を使用する場合、カラム名が同一であることが前提です (大文字と小文字は区別されます) 。作成時点で、materialized view は Kafka エンジンに接続し、読み取りを開始してターゲットテーブルへの行の挿入を開始します。この処理は継続的に実行され、以降に Kafka に挿入されるメッセージも消費されます。必要に応じて、挿入スクリプトを再実行して、Kafka にさらにメッセージを挿入できます。
7
行が挿入されていることを確認する
ターゲットテーブルにデータが存在することを確認します。200,000 行が表示されるはずです。
主な操作
メッセージ消費の停止と再開
Kafka メタデータの追加
_ で始まります。
仮想カラムの完全な一覧はこちらで確認できます。
仮想カラムをテーブルに反映するには、materialized view を削除し、Kafka エンジンのテーブルを再アタッチしてから、materialized view を再作成する必要があります。
Kafka エンジン設定の変更
問題のデバッグ
clickhouse-server.err.log を使用することを推奨します。基盤となるKafkaクライアントライブラリ librdkafka については、設定によりさらに詳細なトレースログを有効にできます。
不正な形式のメッセージの処理
- メッセージのフィールドは文字列として扱います。必要に応じて、materialized view ステートメント内で関数を使ってクレンジングや CAST を行えます。これは本番向けの解決策ではありませんが、一度限りのインジェストには役立つ場合があります。
- トピックから JSON を読み込み、JSONEachRow フォーマットを使用している場合は、設定
input_format_skip_unknown_fieldsを使用してください。データの書き込み時、デフォルトでは、入力データにターゲットテーブルに存在しないカラムが含まれていると、ClickHouse は例外をスローします。ただし、このオプションを有効にすると、こうした余分なカラムは無視されます。これも本番レベルの解決策ではなく、他の利用者を混乱させる可能性があります。 - 設定
kafka_skip_broken_messagesの利用も検討してください。これを使うには、不正な形式のメッセージに対するブロックごとの許容度を、kafka_max_block_sizeを踏まえてユーザーが指定する必要があります。この許容度を超えた場合 (絶対メッセージ数で判定) 、通常どおり例外が発生し、それ以外のメッセージはスキップされます。
配信セマンティクスと重複に関する課題
クォーラムベースのインサート
ClickHouse から Kafka へ
手順
1
行を直接 insert する
まず、ターゲットテーブルの行数を確認してください。200,000行あるはずです。次に、GitHub ターゲットテーブルから行を Kafka テーブルエンジン github_queue に再度 insert します。JSONEachRow フォーマットを利用し、SELECT を 100 行に LIMIT している点に注目してください。GitHub の行数をもう一度カウントし、100増えていることを確認してください。上の図に示されているように、行はまず Kafka テーブルエンジン を介して Kafka に挿入され、その後、同じエンジンによって再度読み込まれ、materialized view によって GitHub ターゲットテーブル に挿入されます!さらに100行が表示されるはずです。
2
materialized view を使う
テーブルにドキュメントが挿入されたとき、materialized view を利用してメッセージを Kafka エンジン (およびトピック) に送ることができます。GitHub テーブルに行が挿入されると、materialized view がトリガーされ、その結果、行は Kafka エンジン に再度挿入され、新しいトピックにも送られます。これも図で見るのが最もわかりやすいでしょう。新しい Kafka トピック 次に、新しい materialized view 元のgithubトピック (Kafka to ClickHouse の一部として作成) にinsertすると、ドキュメントは自動的に “github_clickhouse” トピックに現れます。これをKafkaのネイティブツールで確認してください。たとえば以下では、Confluent Cloudでホストされているトピックに対して、kcat を使用し、githubトピックに100行をinsertします。これは複雑な例ですが、Kafka エンジンと組み合わせて使用した場合の materialized view の威力を示しています。
github_out または同等のものを作成してください。Kafka テーブルエンジン github_out_queue がこのトピックを指していることを確認してください。github_out_mv を作成し、GitHub テーブルを参照するように設定します。これがトリガーされると、上記のエンジンに行が insert されます。これにより、GitHub テーブルへの追加分は新しい Kafka トピック にプッシュされます。github_out トピックを読めば、メッセージが配信されたことを確認できるはずです。クラスターとパフォーマンス
ClickHouseクラスターの使用
パフォーマンスのチューニング
- パフォーマンスは、メッセージサイズ、フォーマット、ターゲットテーブルの types によって異なります。単一のテーブルエンジンで 10 万行/秒は十分に達成可能な水準です。デフォルトでは、メッセージは
kafka_max_block_sizeパラメーターで制御されるブロック単位で読み取られます。これはデフォルトで max_insert_block_size に設定されており、既定値は 1,048,576 です。メッセージが極端に大きい場合を除き、この値はほぼ常に増やすべきです。500k ~ 1M 程度の値も珍しくありません。スループットへの影響をテストして評価してください。 - テーブルエンジンのコンシューマー数は
kafka_num_consumersで増やせます。ただし、デフォルトではkafka_thread_per_consumerを既定値の1から変更しない限り、インサートは単一スレッドで直列化されます。フラッシュが並列に実行されるよう、これを1に設定してください。なお、N 個のコンシューマーを持つ Kafka engine テーブル (kafka_thread_per_consumer=1) を作成することは、それぞれに materialized view があり、kafka_thread_per_consumer=0が設定された N 個の Kafka engine を作成するのと論理的に等価です。 - コンシューマー数の増加は無償ではありません。各コンシューマーは独自のバッファーとスレッドを保持するため、サーバーのオーバーヘッドが増えます。まずは可能であればクラスター全体で線形にスケールさせ、コンシューマーによるオーバーヘッドを意識してください。
- Kafka メッセージのスループットにばらつきがあり、遅延を許容できる場合は、より大きなブロックがフラッシュされるよう
stream_flush_interval_msを増やすことを検討してください。 - background_message_broker_schedule_pool_size は、バックグラウンドタスクを実行するスレッド数を設定します。これらのスレッドは Kafka ストリーミングに使用されます。この設定は ClickHouseサーバーの起動時に適用され、ユーザーセッション中に変更することはできません。既定値は 16 です。ログにタイムアウトが見られる場合は、この値を増やすのが適切なことがあります。
- Kafka との通信には
librdkafkaライブラリが使用されており、このライブラリ自体もスレッドを作成します。そのため、多数の Kafka テーブルやコンシューマーがあると、大量のコンテキストスイッチが発生する可能性があります。この負荷はクラスター全体に分散し、可能であればターゲットテーブルのみをレプリケートするか、1 つのテーブルエンジンで複数のトピックを読み取ることを検討してください。値のリストがサポートされています。1 つのテーブルから複数の materialized view を読み取ることができ、それぞれが特定のトピックのデータをフィルタリングできます。
追加の設定
- Kafka_max_wait_ms - 再試行する前に Kafka からメッセージを読み取る際の待機時間 (ミリ秒) です。ユーザープロファイルレベルで設定し、デフォルト値は 5000 です。