ClickHouse Rust クライアント
Paul Loyd によって当初開発された、ClickHouse に接続するための公式の Rust クライアントです。クライアントのソースコードは GitHub リポジトリ で公開されています。
概要
- 行のエンコード/デコードには
serdeを使用します。 serde属性skip_serializing、skip_deserializing、renameをサポートします。- HTTP トランスポート上で
RowBinaryフォーマットを使用します。- TCP 上の
Nativeへの切り替えを計画しています。
- TCP 上の
- TLS(
native-tlsおよびrustls-tls機能)をサポートします。 - 圧縮および展開(LZ4)をサポートします。
- データの SELECT/INSERT、DDL の実行、およびクライアント側のバッチ処理用 API を提供します。
- ユニットテスト用の便利なモック機能を提供します。
インストール
このクレートを使用するには、Cargo.toml に以下を追加してください:
あわせて crates.io のページ も参照してください。
Cargo features
lz4(デフォルトで有効) —Compression::Lz4とCompression::Lz4Hc(_)バリアントを有効にします。有効な場合、Compression::Lz4はWATCHを除くすべてのクエリでデフォルトとして使用されます。native-tls— OpenSSL にリンクするhyper-tlsを通じて、HTTPSスキームの URL をサポートします。rustls-tls— OpenSSL にリンクしないhyper-rustlsを通じて、HTTPSスキームの URL をサポートします。inserter—client.inserter()を有効にします。test-util— モックを追加します。このサンプル を参照してください。dev-dependenciesでのみ使用してください。watch—client.watch機能を有効にします。詳細は該当セクションを参照してください。uuid— uuid クレートを扱うためにserde::uuidを追加します。time— time クレートを扱うためにserde::timeを追加します。
HTTPS URL 経由で ClickHouse に接続する場合は、native-tls か rustls-tls のいずれかの feature を有効にする必要があります。
両方が有効な場合は、rustls-tls feature が優先されます。
ClickHouse のバージョン互換性
このクライアントは、LTS もしくはそれ以降のバージョンの ClickHouse および ClickHouse Cloud と互換性があります。
v22.6 より古い ClickHouse サーバーは、RowBinary をまれなケースで誤って処理します。
この問題を解決するには、v0.11 以降を使用し、wa-37420 機能を有効にしてください。なお、この機能は新しいバージョンの ClickHouse では使用しないでください。
例
クライアントリポジトリ内の examples で、クライアント利用のさまざまなシナリオをカバーすることを目指しています。概要は examples README で確認できます。
examples や以下のドキュメントに不明な点や不足している点がある場合は、遠慮なくお問い合わせください。
使い方
ch2rs クレートは、ClickHouse から行の型を生成するのに役立ちます。
クライアントインスタンスの作成
作成済みのクライアントを再利用するか、クローンして、基盤となる hyper のコネクションプールを共有するようにしてください。
HTTPS または ClickHouse Cloud への接続
HTTPS 接続は、rustls-tls または native-tls のいずれかの Cargo 機能で動作します。
その後は、通常どおりクライアントを作成します。次の例では、環境変数を使用して接続情報を保持します。
URL にはプロトコルとポートの両方を含める必要があります(例: https://instance.clickhouse.cloud:8443)。
関連情報:
- クライアントリポジトリにある ClickHouse Cloud を利用した HTTPS のサンプル。これはオンプレミス環境での HTTPS 接続にも利用できます。
行を選択する
- プレースホルダ
?fieldsは、Rowのフィールドであるno, nameに置き換えられます。 - プレースホルダ
?は、後続のbind()呼び出しで指定した値に置き換えられます。 - 先頭の 1 行またはすべての行を取得するには、
fetch_one::<Row>()およびfetch_all::<Row>()という便利なメソッドをそれぞれ使用できます。 - テーブル名をバインドするには、
sql::Identifierを使用できます。
注意: 応答全体がストリーミングされるため、カーソルは一部の行を返した後でもエラーを返す可能性があります。このようなケースが発生する場合は、サーバー側でレスポンスバッファリングを有効にするために、query(...).with_option("wait_end_of_query", "1") を試してみてください。詳しくはこちらを参照してください。buffer_size オプションも有用な場合があります。
行を選択する際に wait_end_of_query を使用する場合は注意してください。サーバー側でのメモリ使用量が増加し、全体的なパフォーマンスが低下する可能性が高くなります。
行を挿入する
end()が呼び出されない場合、INSERTは中止されます。- 行はネットワーク負荷を分散するために、ストリームとして順次送信されます。
- ClickHouse は、すべての行が同じパーティションに収まり、かつ行数が
max_insert_block_size未満である場合にのみ、バッチをアトミックに挿入します。
非同期挿入(サーバー側バッチ処理)
受信データをクライアント側でバッチ処理しないようにするには、ClickHouse asynchronous inserts を利用できます。これは、insert メソッドに async_insert オプションを指定する(あるいは Client インスタンス自体に指定して、すべての insert 呼び出しに適用する)だけで実現できます。
こちらも参照してください:
- クライアントリポジトリの 非同期インサートの例。
Inserter 機能(クライアント側バッチ処理)
inserter Cargo フィーチャが必要です。
Inserterは、いずれかのしきい値(max_bytes、max_rows、period)に達した場合、commit()内で進行中の挿入処理を終了します。- 並列インサータによる負荷スパイクを回避するために、
with_period_biasを使用してアクティブなINSERTを終了する間隔にバイアス(ばらつき)を持たせることができます。 - 現在の期間がいつ終了するかを検出するには
Inserter::time_left()を使用できます。ストリームがまれにしかアイテムを出力しない場合は、Inserter::commit()を再度呼び出して制限を再チェックしてください。 - 時間しきい値は quanta クレートを使って実装されており、
inserterの高速化を行います。test-utilが有効な場合は使用されません(そのため、カスタムテストではtokio::time::advance()によって時間を制御できます)。 commit()呼び出し間のすべての行は、同じINSERT文として挿入されます。
挿入を終了/確定したい場合は、フラッシュするのを忘れないでください:
DDL の実行
シングルノードデプロイメント環境では、DDL は次のように実行するだけで十分です。
ただし、ロードバランサーを備えたクラスターデプロイメントや ClickHouse Cloud を利用している場合は、wait_end_of_query オプションを使用して、DDL がすべてのレプリカに適用されるまで待つことを推奨します。これは次のように実行できます。
ClickHouse の設定
with_option メソッドを使用して、さまざまな ClickHouse の設定 を適用できます。例:
query だけでなく、insert および inserter メソッドでも同様に動作します。さらに、同じメソッドを Client インスタンスに対して呼び出すことで、すべてのクエリに適用されるグローバル設定を行うことができます。
クエリ ID
.with_option を使用すると、ClickHouse のクエリログでクエリを識別するための query_id オプションを設定できます。
query のほかに、insert および inserter メソッドでも同様に利用できます。
query_id を手動で設定する場合は、一意であることを確認してください。そのためには UUID を使用するのが適しています。
参考: クライアントリポジトリ内の query_id のサンプル も参照してください。
セッション ID
query_id と同様に、同じセッションでステートメントを実行するために session_id を設定できます。session_id はクライアントレベルでグローバルに設定することも、query、insert、inserter の各呼び出しごとに個別に設定することもできます。
クラスタ構成のデプロイメントでは「スティッキーセッション」がないため、この機能を正しく利用するには 特定のクラスタノード に接続する必要があります。たとえばラウンドロビン方式のロードバランサーでは、後続のリクエストが同じ ClickHouse ノードで処理されることは保証されません。
関連項目: クライアントリポジトリ内の session_id の例 を参照してください。
カスタム HTTP ヘッダー
プロキシ認証を使用している場合やカスタムヘッダーを渡す必要がある場合は、次のように指定できます。
参考: クライアントリポジトリ内の カスタム HTTP ヘッダーの例 も参照してください。
カスタム HTTP クライアント
これは、内部の HTTP 接続プールの設定を調整する際に役立ちます。
この例はレガシーな Hyper API に依存しており、今後変更される可能性があります。
あわせて、クライアントリポジトリ内の custom HTTP client example も参照してください。
データ型
-
(U)Int(8|16|32|64|128)は、対応する(u|i)(8|16|32|64|128)型またはそれらをラップした newtype にマップされます。 -
(U)Int256は直接はサポートされていませんが、回避策があります。 -
Float(32|64)は、対応するf(32|64)またはそれらをラップした newtype にマップされます。 -
Decimal(32|64|128)は、対応するi(32|64|128)またはそれらをラップした newtype にマップされます。符号付き固定小数点数の実装としては、fixnumなどを利用するとより便利です。 -
Booleanはboolまたはそれをラップした newtype にマップされます。 -
Stringは、任意の文字列型またはバイト列型(例:&str,&[u8],String,Vec<u8>、SmartString)にマップされます。独自定義の型もサポートされます。バイト列を保存する場合は、より効率的であるためserde_bytesの使用を検討してください。
FixedString(N)は[u8; N]のようなバイト配列としてサポートされます。
Enum(8|16)はserde_reprによってサポートされています。
UUIDは、serde::uuidを使用してuuid::Uuidと相互マッピングされます。uuidフィーチャ(feature)が必要です。
IPv6はstd::net::Ipv6Addrと相互に対応します。IPv4はserde::ipv4を使用してstd::net::Ipv4Addrと相互に対応します。
Dateはu16またはそれを包む newtype にマッピングされ、1970-01-01からの経過日数を表します。さらに、timeフィーチャを有効にしたうえでserde::time::dateを使用することで、time::Dateもサポートされます。
Date32はi32またはそれをラップした newtype 型との相互変換に対応しており、1970-01-01からの経過日数を表します。また、timeフィーチャーを有効にした上でserde::time::date32を使用することで、time::Dateもサポートされます。
DateTimeはu32またはそれを包む newtype との間でマッピングされ、UNIX エポックからの経過秒数を表します。加えて、time::OffsetDateTimeも、timefeature を必要とするserde::time::datetimeを使用することでサポートされます。
DateTime64(_)はi32またはそれを包む newtype 型との間でマッピングされ、UNIX エポックからの経過時間を表します。 また、timeフィーチャが必要なserde::time::datetime64::*を使用することで、time::OffsetDateTimeもサポートされます。
Tuple(A, B, ...)は(A, B, ...)またはそれをラップする newtype 型との間でマッピングされます。Array(_)は任意のスライス(例:Vec<_>、&[_])との間でマッピングされます。ユーザー定義の新しい型もサポートされています。Map(K, V)はArray((K, V))と同様に動作します。LowCardinality(_)は透過的にサポートされています。Nullable(_)はOption<_>と相互にマッピングされます。clickhouse::serde::*ヘルパーを利用する場合は::optionを追加してください。
Nestedは、名前を付け替えた複数の配列として指定することでサポートされます。
Geo型がサポートされています。Pointはタプル(f64, f64)のように振る舞い、その他の型はPointのスライスにすぎません。
Variant、Dynamic、(新しい)JSONデータ型は現在まだサポートされていません。
モック機能
このクレートは、ClickHouse サーバーのモックや DDL、SELECT、INSERT、WATCH クエリのテスト用ユーティリティを提供します。この機能は test-util フィーチャーを有効にすると利用できます。開発時の依存関係(dev-dependency)としてのみ使用してください。
サンプルコードを参照してください。
トラブルシューティング
CANNOT_READ_ALL_DATA
CANNOT_READ_ALL_DATA エラーの最も一般的な原因は、アプリケーション側の行定義が ClickHouse の定義と一致していないことです。
次のテーブルを考えてみます:
次に、アプリケーション側で EventLog が次のように型の不一致を伴って定義されている場合、例えば:
データを挿入する際、次のエラーが発生することがあります:
この例では、EventLog 構造体を正しく定義することで、この問題は解決されます。
既知の制限事項
Variant、Dynamic、(新しい)JSONデータ型にはまだ対応していません。- サーバーサイドでのパラメータバインディングにはまだ対応していません。進捗は この Issue を参照してください。
お問い合わせ
ご質問やサポートが必要な場合は、Community Slack または GitHub の issue からお気軽にお問い合わせください。