ClickHouse Rust クライアント
Rust 用の ClickHouse 公式クライアントであり、元々は Paul Loyd によって開発されました。クライアントのソースコードは 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(_)バリアントを有効にします。有効になっている場合、WATCHを除くすべてのクエリで、デフォルトとしてCompression::Lz4が使用されます。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 バージョン互換性
このクライアントは、ClickHouse の LTS 以降のバージョンおよび ClickHouse Cloud と互換性があります。
v22.6 より古い ClickHouse サーバーは、RowBinary をまれなケースで誤って処理します。
この問題を解決するには、v0.11 以降を使用し、wa-37420 機能を有効にしてください。注意:この機能は新しい ClickHouse バージョンでは使用すべきではありません。
Examples
クライアントリポジトリ内のexamplesでは、クライアントのさまざまな利用シナリオを網羅することを目的としています。概要は examples README で確認できます。
examples や以下のドキュメントに不明点や不足がある場合は、遠慮なくお問い合わせください。
使用方法
ch2rs クレートは、ClickHouse の行を表す型を生成するのに便利です。
クライアントインスタンスの作成
既存のクライアントを再利用するか、クローンして、基盤となる hyper の接続プールを再利用してください。
HTTPS または ClickHouse Cloud への接続
HTTPS は rustls-tls または native-tls のいずれかの Cargo feature で動作します。
その後、通常どおりクライアントを作成します。次の例では、環境変数を使用して接続情報を保持します。
URL にはプロトコルとポートの両方を含める必要があります。例: https://instance.clickhouse.cloud:8443
参照:
- クライアントリポジトリ内の ClickHouse Cloud を用いた HTTPS の例。これはオンプレミス環境で HTTPS 接続を行う場合にも適用できます。
行の取得
- プレースホルダー
?fieldsは、Rowのフィールドであるno, nameに置き換えられます。 - プレースホルダー
?は、後続のbind()呼び出しで指定された値に置き換えられます。 - 先頭の行またはすべての行を取得するには、それぞれ
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 機能(クライアント側バッチ処理)
Cargo の inserter フィーチャが必要です。
Inserterは、いずれかのしきい値(max_bytes、max_rows、period)に達した場合、commit()内でアクティブなINSERTを終了します。- 並列 inserter による負荷スパイクを避けるため、
with_period_biasを使用してアクティブなINSERTを終了する間隔にバイアスをかけることができます。 - 現在の period がいつ終了するかを検出するには
Inserter::time_left()を使用できます。ストリームがまれにしか要素を出力しない場合は、Inserter::commit()を再度呼び出してしきい値を再確認してください。 - 時間のしきい値は quanta クレートを使用して実装されており、
inserterの高速化に役立ちます。test-utilが有効な場合には使用されません(この場合、カスタムテストではtokio::time::advance()によって時間を制御できます)。 commit()呼び出しの間にあるすべての行は、同じINSERTステートメントで挿入されます。
挿入処理を終了/完了させたい場合は、flush を忘れないでください:
DDL の実行
シングルノードのデプロイメントの場合、DDL は次のように実行すれば十分です。
しかし、ロードバランサー経由の構成や ClickHouse Cloud を利用したクラスターデプロイメントでは、wait_end_of_query オプションを使用して、DDL がすべてのレプリカに適用されるまで待機することを推奨します。これは次のように行います。
ClickHouse の設定
with_option メソッドを使用して、さまざまな ClickHouse settings を適用できます。例えば:
query のほか、insert および inserter メソッドでも同様に動作します。さらに、同じメソッドを Client インスタンスに対して呼び出すことで、すべてのクエリに適用されるグローバルな設定を行うこともできます。
クエリ ID
.with_option を使用すると、ClickHouse のクエリログでクエリを特定できるように、query_id オプションを設定できます。
query 以外にも、insert および inserter メソッドでも同様に動作します。
query_id を手動で設定する場合は、必ず一意になるようにしてください。そのためには UUID を使用するのがよい選択です。
関連項目: クライアントリポジトリ内の query_id example
セッション ID
query_id と同様に、同じセッション内で文を実行するために session_id を設定できます。session_id は、クライアントレベルでグローバルに、または query、insert、inserter の各呼び出しごとに設定できます。
クラスタ構成のデプロイメントでは、"sticky sessions" がないため、この機能を正しく利用するには 特定のクラスタノード に接続されている必要があります。例えば、ラウンドロビン方式のロードバランサーでは、後続のリクエストが常に同じ ClickHouse ノードで処理されることは保証されません。
クライアントリポジトリ内の session_id の例も参照してください。
カスタム HTTP ヘッダー
プロキシ認証を使用している場合やカスタムヘッダーを渡す必要がある場合は、次のように指定できます。
関連項目: クライアントリポジトリ内の custom HTTP headers のサンプル を参照してください。
カスタム 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との相互変換に対応しています。uuidfeature フラグが必要です。
IPv6はstd::net::Ipv6Addrと相互変換されます。IPv4はserde::ipv4を使用してstd::net::Ipv4Addrと相互変換されます。
Dateはu16またはそれを包む newtype との相互変換を行い、1970-01-01からの経過日数を表します。さらに、time::Dateも、timefeature を有効にした上でserde::time::dateを使用することでサポートされます。
Date32はi32またはそれをラップした newtype との相互変換に対応しており、1970-01-01からの経過日数を表します。また、timeフィーチャが有効であれば、serde::time::date32を利用してtime::Dateもサポートされます。
DateTimeはu32またはそれを包んだ newtype と相互にマッピングされ、UNIX エポックからの経過秒数を表します。また、timefeature を有効にすることで利用可能になるserde::time::datetimeを用いることで、time::OffsetDateTimeもサポートされます。
DateTime64(_)はi32またはそれをラップした newtype にマッピングされ、UNIX エポックからの経過時間を表します。また、time::OffsetDateTimeも、timefeature を有効にしたうえでserde::time::datetime64::*を利用することでサポートされます。
Tuple(A, B, ...)は(A, B, ...)またはそれをラップした newtype との相互変換になります。Array(_)は任意のスライス(例:Vec<_>、&[_])との相互変換になります。newtype もサポートされています。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 フィーチャーを有効にすることで使用できます。Cargo の dev-dependencies としてのみ使用してください。
例を参照してください。
トラブルシューティング
CANNOT_READ_ALL_DATA
CANNOT_READ_ALL_DATA エラーの最も一般的な原因は、アプリケーション側の行定義が ClickHouse 側の定義と一致していないことです。
次のテーブルを考えてみましょう。
次に、アプリケーション側で EventLog が型の不一致を起こす形で定義されている場合、例えば次のようになります。
データの挿入時に、次のエラーが発生することがあります。
この例では、EventLog 構造体を正しく定義することで修正されます。
既知の制限事項
Variant、Dynamic、(新しい)JSONデータ型にはまだ対応していません。- サーバーサイドのパラメータバインディングにはまだ対応していません。進捗の追跡については this issue を参照してください。
お問い合わせ
ご質問やサポートが必要な場合は、Community Slack または GitHub の issue からお気軽にご連絡ください。