はじめに
ClickHouse の概念
ClickHouse では、テーブルはデータベースに属します。デフォルトでは
default データベースが使用されますが、これは OpenTelemetry collector で変更できます。
最低限、次の ClickHouse の基本事項を理解しておく必要があります。
これらの概念は、ClickHouse のパフォーマンスの中核を成します。データがどのように書き込まれるか、ディスク上でどのように構成されるか、そしてクエリ時に ClickHouse がどれだけ効率的に不要なデータの読み取りをスキップできるかは、これらによって決まります。このガイドで扱うあらゆる最適化、たとえば マテリアライズドカラム、スキップ索引、主キー、projections、materialized view は、こうした基本的な仕組みの上に成り立っています。
チューニングを始める前に、以下の ClickHouse ドキュメントを確認しておくことをお勧めします。
- ClickHouse でのテーブル作成 - テーブルの簡単な入門です。
- Parts
- Partitions
- Merges
- 主キー/索引
- ClickHouse がデータを保存する方法: parts と グラニュール - ClickHouse でデータがどのように構成され、どのようにクエリされるかを詳しく説明した、より高度なガイドです。グラニュール と主キーも詳しく扱っています。
- MergeTree- コマンドや内部仕様の理解に役立つ高度な MergeTree リファレンスガイドです。
最適化 1. 頻繁にクエリされる属性をマテリアライズする
LogAttributes、ScopeAttributes、ResourceAttributes 内で頻繁にクエリされる属性を特定し、マテリアライズドカラムを使ってそれらをトップレベルのカラムに昇格させることです。
この最適化だけでも、ClickStack のデプロイメントを 1 日あたり数十テラバイト規模までスケールさせるのに十分な場合が多く、より高度なチューニング手法を検討する前に適用すべきです。
属性をマテリアライズする理由
Map(String, String) カラムに格納します。これは柔軟性が高い一方で、マップのサブキーをクエリする際にはパフォーマンス上の重要な影響があります。
Map カラムから単一のキーをクエリする場合、ClickHouse はディスクからマップカラム全体を読み込む必要があります。マップに多数のキーが含まれていると、専用のカラムを読み込む場合に比べて不要な IO が発生し、クエリも遅くなります。
頻繁にアクセスされる属性をマテリアライズすると、挿入時に値を抽出して独立したカラムとして保存できるため、このオーバーヘッドを回避できます。
マテリアライズドカラムの特長:
- 挿入時に自動的に計算される
- INSERT ステートメントで明示的に設定できない
- 任意の ClickHouse 式をサポートする
- String から、より効率的な数値型や日付型へ型変換できる
- スキップ索引と主キーを利用できる
- マップ全体へのアクセスを避けることでディスク読み取りを削減できる
ClickStack は、マップから抽出されたマテリアライズドカラムを自動的に検出し、ユーザーが元の属性パスを引き続きクエリしている場合でも、クエリ実行時に透過的にそれらを使用します。
例
ResourceAttributes に格納される、トレース向けのデフォルトの ClickStack スキーマを見てみましょう。
ResourceAttributes.k8s.pod.name:"checkout-675775c4cc-f2p9c" のように指定します。
その結果、次のようなSQL述語になります。
ResourceAttributes カラム全体を読み取る必要があります。Map に多くのキーが含まれている場合、このカラムは非常に大きくなる可能性があります。
この属性を頻繁にクエリする場合は、トップレベルのカラムとしてマテリアライズする必要があります。
挿入時にポッド名を抽出するには、マテリアライズドカラムを追加します:
PodName として保存されます。
これにより、ユーザーは Lucene 構文を使ってポッド名を効率よくクエリできるようになります。たとえば PodName:"checkout-675775c4cc-f2p9c" のように指定できます。
新たに挿入されるデータでは、これにより map へのアクセスが不要になり、I/O も大幅に削減されます。
ただし、ユーザーが元の属性パス、たとえば ResourceAttributes.k8s.pod.name:"checkout-675775c4cc-f2p9c" を使って引き続きクエリする場合でも、ClickStack はクエリを内部で自動的に書き換え、マテリアライズされた PodName カラムを使用します。つまり、次の述語が使われます:
デフォルトでは、マテリアライズドカラムは
SELECT * queries には含まれません。これにより、クエリ結果を常にそのテーブルに再挿入できるという不変条件が保たれます。過去データのマテリアライズ
system.mutations テーブルで確認できます。
is_done = 1 になるまで待機します。
最適化 2. スキップ索引の追加
- TraceId、セッション識別子、attribute キー、値など、カーディナリティの高い文字列のフィルタリング
*AttributeItemsカラムの テキスト索引 によって高速化される Map サブキーのフィルタリング- スパンの所要時間など、数値範囲のフィルタリング
text(tokenizer = 'array') 索引を使用し、さらに全文検索用に lower(Body) に text(tokenizer = 'splitByNonAlpha') 索引を追加しています。完全な DDL については、“ClickStack で使用されるテーブルとスキーマ” を参照してください。
ブルームフィルタ
PodName:"checkout-675775c4cc-f2p9c" のようなクエリで読み取るデータ量を削減できる可能性があります。
ブルームフィルタが最も効果を発揮するのは、特定の値が比較的少数のパーツにしか現れないような値の分布になっている場合です。これは、ポッド名、トレースID、セッション識別子のようなメタデータが時間と相関し、その結果、テーブルの順序キーに沿ってクラスター化されるオブザーバビリティのワークロードで自然によく見られます。
すべてのスキップ索引と同様に、ブルームフィルタも選択的に追加し、実際のクエリパターンに対して検証して、測定可能な効果が得られることを確認する必要があります。詳細は”スキップ索引の有効性を評価する”を参照してください。
テキスト索引
WHERE 条件に照らして評価する必要があります。テキスト索引は、トークンをパーツ内の正確なオフセットに対応付ける転置索引です。グラニュールではなくオフセットを評価し、偽陽性も発生しないため、通常は基になるカラムを読み込まずに WHERE 条件を満たすかどうかを判断できます。これは direct read と呼ばれる最適化です。データの読み込みは多くの場合、クエリ時間の最大の要因となるため、direct read によってクエリのレイテンシを大きく減らせます。
さらに、テキスト索引自体もクエリ可能で、ClickStack のオートコンプリートやその他のイントロスペクションを支えています。
ほとんどの ClickStack のパターンは、次の 2 つのトークナイザーでカバーできます。
Map と Array カラム向けの Array トークナイザー
array トークナイザーを使って mapKeys とマテリアライズされた item の配列に索引を作成します:
ログのボディ向け splitByNonAlpha
Body カラムに対する全文検索では、splitByNonAlpha テキスト
索引を使用すると効果的です。ClickStack ではこの索引を lower(Body) に定義しているため、
大文字と小文字を区別しない Lucene 検索で利用できます:
lower(Body) 上の text(tokenizer = 'splitByNonAlpha') 索引を
検出すると、error や
"connection refused" のような暗黙的なカラム指定の Lucene クエリを hasAllTokens(lower(Body), lower(...)) に書き換えます。これにより、
Body カラム全体を読み込むことなく、索引だけで処理できます。ほとんどの
オブザーバビリティのログワークロードでは、これは利用可能な
フィルタリング高速化の中で最も大きな効果があります。
テキスト索引と
tokenbf_v1 の比較古い tokenbf_v1 索引タイプ (デフォルトのトレーススキーマで
lower(SpanName) に今も使われています) は機能面では似ていますが、ClickHouse 26.2
以降では非推奨です。新しいテキスト検索索引には text(tokenizer = ...) を使用してください。デフォルトのログスキーマにおけるテキスト索引
otel_logs スキーマには、前述のテキスト索引がすべて含まれています。具体的には、TraceId、各 mapKeys(...) および *AttributeItems 配列に対する text(tokenizer = 'array') と、全文検索用の lower(Body) に対する text(tokenizer = 'splitByNonAlpha') です。正式な DDL については、“ClickStack で使用されるテーブルとスキーマ” を参照してください。同じスキーマを以下にも再掲しています。
Min-max 索引
SpanAttributes 内の Kafka オフセットが頻繁にクエリされるとします。
スキップ索引をマテリアライズする
スキップ索引のマテリアライズスキップ索引のマテリアライズは、通常は軽量で安全に実行できます。特に minmax 索引ではその傾向が顕著です。大規模なデータセットに対する ブルームフィルタ 索引では、リソース使用量をより適切に制御するため、パーティション単位でマテリアライズするほうがよい場合があります。例:
is_done = 1 になるまで待ちます。
完了したら、索引データが作成されていることを確認します。
0.01 から 0.05 に引き上げると、より小さく、より高速に評価できる索引になりますが、その代わりプルーニングの効果は弱まります。スキップできるグラニュールは少なくなる可能性がありますが、索引評価が速くなることで、クエリ全体のレイテンシが改善する場合があります。
したがって、ブルームフィルタ パラメータの調整はワークロード依存の最適化であり、実際のクエリパターンと本番環境に近いデータ量で検証する必要があります。
スキップ索引の詳細については、ガイド “ClickHouse のデータスキッピングインデックスを理解する” を参照してください。
スキップ索引の有効性を評価する
EXPLAIN indexes = 1 を使用することです。これにより、クエリプランの各段階で、どれだけの パーツ と グラニュール が除外されるかを確認できます。多くの場合、主キーによって検索範囲がすでに絞り込まれたあとに、Skip ステージで グラニュール が大幅に減っているのが理想です。スキップ索引はパーティションプルーニングと主キープルーニングのあとに評価されるため、その効果は、残ったパーツと グラニュール に対してどれだけ削減できたかで見るのが最も適切です。
EXPLAIN を使えばプルーニングが実際に行われているかは確認できますが、それだけで全体として高速化されるとは限りません。スキップ索引の評価にはコストがかかり、特に索引が大きい場合はその影響が大きくなります。実際に性能が向上していることを確認するため、索引を追加してマテリアライズする前後で、必ずクエリをベンチマークしてください。
たとえば、デフォルトの Traces スキーマに含まれる、TraceId 用のデフォルトのブルームフィルタースキップ索引を見てみましょう。
EXPLAIN indexes = 1 を使うと確認できます:
FORMAT Null を使用し、実行結果の再現性を保つためにクエリ条件 cache を無効にします:
use_query_condition_cache を無効にすると、キャッシュされたフィルタリング判定の影響を結果が受けないようにでき、use_skip_indexes = 0 を設定すると、比較のためのクリーンなベースラインを確保できます。絞り込みが効果的で、かつ索引の評価コストが低い場合は、上の例のように、索引付きクエリのほうが大幅に高速になるはずです。
スキップ索引を追加するタイミング
IN フィルタを高速化できるだけでなく、全文検索や Map direct read 最適化 で使われるトークンベースの条件 (hasToken、hasAllTokens、has) にも対応します。まだテキスト索引をサポートしていない古いクラスターでは、ブルームフィルタは引き続き有力な選択肢です。
ブルームフィルタは、各値の出現頻度が比較的低い高カーディナリティの文字列カラム、つまり検索対象の値がほとんどのパーツやグラニュールに含まれていない場合に最も効果を発揮します。経験則として、ブルームフィルタが有望なのは、そのカラムに少なくとも 10,000 個の異なる値がある場合で、100,000 個以上の異なる値があるとさらに効果が高いことがよくあります。また、一致する値が少数の連続したパーツにまとまっている場合にも有効で、これは通常、そのカラムが順序付けキーと相関しているときに起こります。とはいえ、実際の効果はケースバイケースです。実環境での検証に勝るものはありません。
Optimization 3. Map direct read
LogAttributes['k8s.pod.name'] = 'checkout' のように Map のサブキーでフィルタすると、ClickHouse はディスクから LogAttributes Map カラム全体を読み込み、述語を評価するためにすべての行を展開する必要があります。頻繁にクエリされる属性のマテリアライズ
は、事前に把握しているキーについてはこの問題を解決できますが、ユーザーがその場で任意にフィルタする属性には対応しきれません。
スキーマに mapKeys と mapValues の索引があっても、それらの索引で分かるのは、ある行に特定のキーがあるか、特定の値があるかまでであり、そのキーと値が同じエントリに属しているかどうかまでは分かりません。言い換えると、mapKeys は mapContainsKey(ResourceAttributes, 'foo') に答え、mapValues は mapContainsValue(ResourceAttributes, 'bar') に答えますが、どちらも ResourceAttributes['foo'] = 'bar' には答えられません。
キーと値を単一の Array(String) カラムに連結することで、Map direct read 最適化では、基になる map を読み込まずに ResourceAttributes['foo'] = 'bar' に答えられるようになります。Map は大きくなりがちで、データ量の増加に伴ってサイズも増大します。さらに、これをアプリケーションレベルのクエリの書き換えと組み合わせることで、任意の Map サブキーに対する等価フィルタは、その索引を利用する単一の has(...) 呼び出しになり、クエリ時の Map のデシリアライゼーションが不要になります。加えて、発生するストレージコストはテキスト索引分のみです。基になるカラムは ALIAS カラムであり、保存されないためです。
この最適化は自動的に適用されます。ClickStack では、デフォルトの logs テーブルと trace テーブルに必要なカラムと索引が含まれており、接続先の ClickHouse server が基盤となる Primitive をサポートしている場合、実行時に Map の添字フィルタを書き換えます。スキーマにこれらのカラムが含まれていない場合や、デフォルト以外にも高速化したい追加の Map カラムがある場合は、以下を読んで有効化してください。
スキーマ
= で連結した Array(String) ALIAS カラムを定義します:
ALIAS 形式では、この配列によってディスク上の使用容量は一切増えません。ClickHouse はこれを
クエリ実行時および索引構築時に計算します。ALIAS カラム上の text(tokenizer = 'array') スキップ索引は、
各 key=value ペアごとに 1 つのトークンを格納し、ClickHouse はこれを使って元の Map にアクセスすることなく
不要なグラニュールを読み飛ばします:
クエリの書き換え
LogAttributeItems のテキスト索引が利用され、key=value
トークンを含まない行全体が刈り込まれ、一致しない行では
元の LogAttributes Map がデシリアライズされることもありません。高カーディナリティの
オブザーバビリティ ワークロードでは、通常、Map の添字アクセスに比べて I/O を
1 桁分削減できます。
この書き換えは自動的に行われるため、LogAttributes['key'] を参照する保存クエリ、ダッシュボード、アラートは
何も変更しなくても高速化の恩恵を受けられます。
ClickHouse のバージョン要件
SELECT version()、接続ごとにキャッシュ)
を検出し、サーバーがしきい値以上の場合にのみ
書き換え後の形式を出力します。古いサーバーでは、自動的に元の
Map 添字形式にフォールバックします。
なぜ MATERIALIZED ではなく ALIAS なのかitems 配列は、Map カラムにすでに存在するデータを参照するためのものです。
これを 2 回保存すると — 1 回は Map に、もう 1 回は配列に —
新しいクエリパターンが使えるようになるわけでもないのに、書き込み I/O だけが 2 倍になります。
ALIAS カラム上のテキスト索引は、同じ元データから挿入時に
構築されるため、この最適化でディスクに追加されるのは
索引の分だけです。最適化 4. 主キーの変更
用語に関する補足このドキュメント全体では、「ソートキー」という用語を「主キー」と同じ意味で使っています。厳密には ClickHouse では両者は異なりますが、ClickStack では通常、テーブルの
ORDER BY 句で指定される同じカラムを指します。詳細は、ソートキーと異なる主キーの選び方についての ClickHouse ドキュメント を参照してください。- ログ (
otel_logs) -(toStartOfFiveMinutes(Timestamp), ServiceName, Timestamp) - トレース (
otel_traces) -(ServiceName, SpanName, toDateTime(Timestamp))
主キーの選択
デフォルトの主キーの変更デフォルトの主キーは、ほとんどのケースで十分です。変更は慎重に行い、クエリパターンを明確に理解したうえでのみ実施してください。主キーを変更すると、他のワークフローのパフォーマンスが低下する可能性があるため、テストは不可欠です。
- 一般的なフィルタ条件やアクセスパターンに合うカラムを選択します。たとえば、オブザーバビリティの調査を通常は特定のカラム (例: ポッド名) で絞り込むことから始める場合、そのカラムは
WHERE句で頻繁に使用されます。使用頻度の低いカラムよりも、こうしたカラムを優先してキーに含めてください。 - フィルタ時に全行の大部分を除外できるカラムを優先します。これにより、読み込む必要のあるデータ量を減らせます。サービス名やステータスコードは有力な候補になることがよくあります。後者については、大半の行を除外できる値でフィルタする場合に限ります。たとえば、多くのシステムでは 200 コードでフィルタすると大半の行に一致しますが、500 エラーであれば一致するのは小さな部分集合です。
- テーブル内の他のカラムと高い相関がある可能性の高いカラムを優先します。これにより、それらの値も連続して格納されやすくなり、圧縮の改善につながります。
- ソートキーに含まれるカラムに対する
GROUP BY(チャート用の集計) およびORDER BY(ソート) の操作は、メモリ効率が向上する場合があります。
主キーの変更
ServiceName の前に SeverityText カラムを含む新しい主キーを持つログテーブルを簡単に作成する方法を示しています。
1
新しいテーブルを作成する
ORDER BY と主キー上記の例では、
PRIMARY KEY と ORDER BY の両方を指定する必要があります。
ClickStack では、これらはほとんどの場合同じです。
ORDER BY は物理的なデータレイアウトを制御し、PRIMARY KEY はスパースインデックスを定義します。
まれに、非常に大規模なワークロードでは両者が異なることもありますが、ほとんどのユーザーは一致させておくべきです。既存データを新しいテーブルにバックフィルすることは、大規模環境ではめったに見合いません。通常はコンピュートと I/O のコストが高く、性能向上のメリットに見合わないためです。代わりに、古いデータは有効期限 (TTL)で期限切れになるままにし、新しいデータが改善されたキーの恩恵を受けるようにしてください。
SeverityText を導入する同じ例を使用します。この場合は、新しいデータ用のテーブルを作成し、履歴分析のために古いテーブルは保持します。
1
2
Merge テーブルを作成する
Merge engine は (MergeTree と混同しないでください) 、それ自体ではデータを保存しませんが、複数の他のテーブルを同時に読み取ることができます。currentDatabase() は、コマンドを正しいデータベースで実行していることを前提としています。そうでない場合は、データベース名を明示的に指定してください。otel_logs のデータが返されることを確認できます。3
Merge テーブルを読み取るように ClickStack UI を更新する
ログのログソースで使用するテーブルとしてotel_logs_merge を設定するよう、ClickStack UI を更新します。この時点では、書き込みは引き続き元の主キーを持つ otel_logs に対して行われ、読み取りには Merge テーブルが使用されます。ユーザーに見える変更はなく、インジェストへの影響もありません。4
テーブルを入れ替える
ここでは、EXCHANGE ステートメントを使用して、otel_logs テーブルと otel_logs_23_01_2025 テーブルの名前をアトミックに入れ替えます。otel_logs テーブルに対して行われます。既存のデータは otel_logs_23_01_2025 に残り、Merge テーブル経由で引き続きアクセスできます。この接尾辞は変更が適用された日付を示しており、そのテーブルに含まれる最新の timestamp を表しています。この手順により、インジェストを中断することなく、またユーザーに見える影響もなく主キーを変更できます。SeverityText ではなく SeverityNumber を主キーに含めるべきだと判断した場合です。以下の手順は、主キーの変更が必要になるたびに何度でも繰り返し応用できます。
1
2
テーブルを入れ替える
ここでは、EXCHANGE ステートメントを使用して、otel_logs テーブルと otel_logs_30_01_2025 テーブルの名前をアトミックに入れ替えます。otel_logs テーブルに対して行われます。古いデータは otel_logs_30_01_2025 に残り、merge テーブル経由で引き続きアクセスできます。不要になったテーブル有効期限 (TTL) ポリシーを設定している場合 (推奨) 、書き込みを受けなくなった古い主キーのテーブルは、データの有効期限切れに伴って徐々に空になります。これらのテーブルは監視し、データがなくなった段階で定期的にクリーンアップする必要があります。現時点では、このクリーンアップは手動で行います。
blockカラムによる行ルックアップの高速化
(_block_number, _block_offset) の組を持つようになります。ClickStack UI でログの行をクリックして詳細パネルを開くと、ClickStack
はその 1 行を取得するための後続クエリを発行します。block カラムがない場合、その
行の WHERE 句には、通常は主キー
に Body と SeverityText を加えた、行を一意に特定するのに十分な数のカラムを含める必要があります。block カラムがある場合は、
主キーに _block_number と _block_offset を加えるだけで十分です。Body のような大きな
カラムはルックアップ時に読み取られないため、結果としてクエリが高速化されます。
ClickStack はテーブルの CREATE ステートメントからこの設定を検出し、
両方のカラムが有効になっている場合は、より簡潔な WHERE 句を自動的に生成します。アプリケーションの
設定を変更する必要はありません。
既存のログまたはトレース テーブルでこの最適化を有効にするには:
ALTER 後に書き込まれたデータに適用されます。既存のパーツは、
マージによって書き換えられるまでは引き続き古い行単位のルックアップを使用します。
最適化 5. materialized view の活用
最適化 6. PROJECTION の活用
ORDER BY キーとは異なる独自のプライマリインデックスを持ち、元の並び順に合わないアクセスパターンに対しても、ClickHouse がより効率よくデータを絞り込めるようになります。
materialized view でも、異なるソートキーを持つ別のターゲットテーブルに行を明示的に書き込むことで、同様の効果を得られます。重要な違いは、PROJECTION は ClickHouse によって自動かつ透過的に維持される のに対し、materialized view は ClickStack が意図的に登録し、選択して使う必要のある明示的なテーブルだという点です。
クエリが基となるテーブルを対象にすると、ClickHouse は基底レイアウトと利用可能な PROJECTION を評価し、それぞれのプライマリインデックスを確認したうえで、正しい結果を返しつつ読み取る グラニュール 数が最も少ないレイアウトを選択します。この判断はクエリアナライザによって自動的に行われます。
したがって ClickStack では、PROJECTION は 純粋なデータの並べ替え に最も適しており、具体的には次のような場合です。
- アクセスパターンがデフォルトの主キーと本質的に異なる
- 単一のソートキーですべてのワークフローをカバーするのが現実的でない
- 最適な物理レイアウトを ClickHouse に透過的に選択させたい
PROJECTION の例
ワイルドカードを使用上記の PROJECTION の例では、ワイルドカード (
SELECT *) を使用しています。選択するカラムを一部に絞ると書き込み時のオーバーヘッドは減らせますが、その一方で PROJECTION を利用できる場面も限られます。というのも、それらのカラムだけで完全に処理できるクエリしか対象にならないためです。ClickStack では、その結果 PROJECTION の用途がごく限られたケースに狭まりがちです。このため、一般には適用範囲を最大化するためにワイルドカードを使うことが推奨されます。プロジェクションのマテリアライズには長時間を要し、多くのリソースを消費する可能性があります。オブザーバビリティデータは通常、有効期限 (TTL) によって期限切れになるため、これは本当に必要な場合にのみ実行してください。ほとんどの場合、プロジェクションは新しく取り込まれたデータにのみ適用されるようにしておけば十分で、直近 24 時間など、最も頻繁にクエリされる時間範囲の最適化に役立ちます。
SELECT *) を単純に並べ替えたものを表しており、クエリのフィルタ条件がプロジェクションの ORDER BY と強く一致している場合に、最も確実に機能します。
TraceId でフィルタリングし (特に等価条件) 、かつ時間範囲を含むクエリでは、上記のプロジェクションの効果が期待できます。たとえば次のとおりです。
TraceId に条件をかけないクエリや、プロジェクションの並び順キーの先頭にない他の次元で主に絞り込むクエリでは、通常は効果がなく、代わりにベースレイアウト経由で読み取られることがあります。
プロジェクションには、集計結果を格納することもできます (materialized view と同様です) 。ただし ClickStack では、プロジェクションベースの集計は一般に推奨されません。どの PROJECTION が選ばれるかは ClickHouse アナライザに依存するため、利用を制御しにくく、挙動も把握しづらいからです。代わりに、ClickStack がアプリケーション層で明示的に登録し、意図して選択できる materialized view を優先してください。
コストと指針
- 挿入時のオーバーヘッド: 異なる順序キーを持つ
SELECT *PROJECTION では、実質的にデータを2回書き込むことになるため、書き込み I/O が増加し、インジェストを維持するには追加の CPU とディスクスループットが必要になる場合があります。 - 必要な場合に限って使用: PROJECTION は、アクセスパターンが明確に異なり、2つ目の物理的な順序付けによって多くのクエリで有意なプルーニング効果が得られる場合に使うのが最適です。たとえば、2つのチームが同じデータセットに対して根本的に異なる方法でクエリするケースです。
- ベンチマークで検証する: ほかのチューニングと同様に、PROJECTION を追加してマテリアライズする前後で、実際のクエリレイテンシとリソース使用量を比較してください。
_part_offset を使った軽量 PROJECTION
ClickStack における軽量 PROJECTION はベータです
_part_offset-based 軽量 PROJECTION は、ClickStack のワークロードには推奨されません。ストレージ使用量と書き込み I/O は削減できますが、その一方でクエリ時のランダムアクセスが増える可能性があり、オブザーバビリティ規模の本番環境での挙動は現在も評価中です。この推奨事項は、機能の成熟と運用データの蓄積に伴って変更される可能性があります。_part_offset ポインタのみを格納する、より軽量な PROJECTION もサポートされています。これによりストレージのオーバーヘッドを大幅に削減でき、最近の改善によって グラニュール 単位のプルーニングも可能になったため、真のセカンダリ索引により近い動作をするようになりました。詳細は次を参照してください。
代替手段
- OpenTelemetry collector を設定し、異なる
ORDER BYキーを持つ 2 つのテーブルに書き込むようにして、各テーブルに対して個別の ClickStack ログソースを作成する。 - materialized view をコピーパイプラインとして作成する。つまり、メインテーブルに materialized view をアタッチし、生の行を別のソートキーを持つ secondary table にそのまま SELECT する (非正規化またはルーティングのパターン) 。このターゲットテーブル用のログソースを作成します。例は こちら を参照してください。