メインコンテンツまでスキップ
メインコンテンツまでスキップ

概要

ClickHouse と OLTP データベースにおけるデータ更新の違い

データの更新に関して言えば、ClickHouse と OLTP データベースは、その基盤となる設計思想と対象となるユースケースの違いから大きく異なります。例えば、行指向で ACID 準拠のリレーショナルデータベースである PostgreSQL は、Multi-Version Concurrency Control (MVCC) のようなメカニズムを通じてデータの整合性と一貫性を確保し、堅牢なトランザクショナルな更新および削除操作をサポートしています。これにより、高い同時実行環境でも安全かつ信頼できる修正が可能になります。

これに対して、ClickHouse は読み取り重視の分析と高スループットの追加オンリー操作に最適化された列指向データベースです。もともとインプレースでの更新や削除をサポートしていますが、高い I/O を避けるためには慎重に使用する必要があります。代わりに、テーブルを再構築して削除および更新を追加操作に変換することも可能で、これにより非同期で処理されたり、あるいは読み取り時に展開されたりするため、高スループットのデータ取り込みと効率的なクエリ性能を重視し、リアルタイムでのデータ操作に依存しないことが反映されます。

ClickHouse でのデータ更新方法

ClickHouse でデータを更新する方法はいくつかあり、それぞれに利点と性能特性があります。適切な方法は、データモデルと更新するデータの量に基づいて選択するべきです。

両方の操作において、提出された変異の数が一定の時間間隔においてバックグラウンドで処理される変異の数を常に超える場合、適用される必要のある非物質化の変異のキューが成長し続けます。これにより、最終的には SELECT クエリの性能が劣化します。

要約すると、更新操作は慎重に発行されるべきで、変異のキューは system.mutations テーブルを使用して注意深く監視されるべきです。OLTP データベースのように頻繁に更新を発行しないでください。頻繁な更新の要件がある場合は、ReplacingMergeTree を参照してください。

メソッド構文使用時期
Update mutationALTER TABLE [table] UPDATEデータをディスクに即座に更新しなければならない場合に使用します(例:コンプライアンスのため)。SELECT パフォーマンスに悪影響を及ぼします。
Lightweight updatesUPDATE [table] SET ... WHERE小規模なデータの更新(テーブルの最大〜10%)に使用します。全カラムを書き換えることなく、即時可視性のためのパッチパーツを生成します。SELECT クエリにはオーバーヘッドが追加されますが、予測可能なレイテンシがあります。現在は実験的です。
On-the-fly updatesALTER TABLE [table] UPDATESET apply_mutations_on_fly = 1; を使用して有効にします。小規模なデータの更新に使用します。行は、以降のすべての SELECT クエリで更新されたデータとともに即座に返されますが、最初はディスク上では内部的にのみ更新されたとマーキングされます。
ReplacingMergeTreeENGINE = ReplacingMergeTree大量のデータを更新する場合に使用します。このテーブルエンジンは、マージ時のデータ重複排除に最適化されています。
CollapsingMergeTreeENGINE = CollapsingMergeTree(Sign)個別の行を頻繁に更新する場合や、時間の経過とともに変化するオブジェクトの最新の状態を維持する必要があるシナリオに使用します。例えば、ユーザー活動や記事の統計を追跡する場合です。

更新変異

更新変異は ALTER TABLE ... UPDATE コマンドを通じて発行できます。例えば、

ALTER TABLE posts_temp
        (UPDATE AnswerCount = AnswerCount + 1 WHERE AnswerCount = 0)

これは非常に I/O 集約型で、WHERE 式に一致するすべてのパーツを書き換えます。このプロセスには原子的な性質はありません。パーツは、変更が準備でき次第、変更されたパーツに置き換えられ、変異中に開始された SELECT クエリは、すでに変更されたパーツのデータとまだ変更されていないパーツのデータを見ることになります。ユーザーは systems.mutations テーブルを介して進行状況の状態を追跡できます。これらは I/O 集約型の操作であり、クラスターの SELECT パフォーマンスに影響を与える可能性があるため、慎重に使用する必要があります。

更新変異 について詳しく読む。

軽量更新

軽量更新は、行を「パッチパーツ」を使用して更新する ClickHouse の機能です。これは、従来の変異のように全カラムを書き換えるのではなく、更新されたカラムと行のみを含む特別なデータパーツから成ります。軽量更新の主な特徴:

  • 標準の UPDATE 構文を使用し、マージを待つことなく即座にパッチパーツを生成します
  • 更新された値は、パッチの適用を通じて SELECT クエリで即座に可視化されますが、物理的には次回のマージ時にのみ具現化されます
  • 予測可能なレイテンシを持つ小規模な更新(最大〜10%のテーブル)用に設計されています
  • パッチを適用する必要のある SELECT クエリにオーバーヘッドが追加されますが、全カラムの書き換えを回避します

詳細については、"軽量 UPDATE 文" を参照してください。

オン・ザ・フライ更新

オン・ザ・フライ更新は、行を即座に更新するメカニズムを提供し、以降の SELECT クエリは自動的に変更された値を返します(これにはオーバーヘッドが発生し、クエリ速度が遅くなる可能性があります)。これにより、通常の変異の原子的制限に対応します。以下に例を示します:

SET apply_mutations_on_fly = 1;

SELECT ViewCount
FROM posts
WHERE Id = 404346

┌─ViewCount─┐
│   26762   │
└───────────┘

1 row in set. Elapsed: 0.115 sec. Processed 59.55 million rows, 238.25 MB (517.83 million rows/s., 2.07 GB/s.)
Peak memory usage: 113.65 MiB.

-increment count
ALTER TABLE posts
        (UPDATE ViewCount = ViewCount + 1 WHERE Id = 404346)

SELECT ViewCount
FROM posts
WHERE Id = 404346

┌─ViewCount─┐
│       26763   │
└───────────┘

1 row in set. Elapsed: 0.149 sec. Processed 59.55 million rows, 259.91 MB (399.99 million rows/s., 1.75 GB/s.)

オン・ザ・フライ更新では、データを更新するために変異がまだ使用されることに注意してください。これはただ即座には具現化されず、SELECT クエリで適用されます。バックグラウンドで非同期プロセスとして適用され、変異と同じ重いオーバーヘッドが発生するため、これは I/O 集約型の操作であり、慎重に使用する必要があります。この操作に使用できる式も限られています(詳細については こちら を参照してください)。

オン・ザ・フライ更新 について詳しく読む。

CollapsingMergeTree

更新が高コストである一方で、挿入を活用して更新を実行できるという考え方から、CollapsingMergeTree テーブルエンジンは、特定の行を削除(カラプス)することで更新する方法として、sign カラムとともに使用することができます。 sign カラムに -1 が挿入されると、全行が削除されます。 sign カラムに 1 が挿入されると、ClickHouse はその行を保持します。 更新対象の行は、テーブル作成時に使用するソートキーに基づいて識別されます。

CREATE TABLE UAct
(
    UserID UInt64,
    PageViews UInt8,
    Duration UInt8,
    Sign Int8 -- A special column used with the CollapsingMergeTree table engine
)
ENGINE = CollapsingMergeTree(Sign)
ORDER BY UserID

INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1)
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1) -- sign = -1 signals to update the state of this row
INSERT INTO UAct VALUES (4324182021466249494, 6, 185, 1) -- the row is replaced with the new state

SELECT
    UserID,
    sum(PageViews * Sign) AS PageViews,
    sum(Duration * Sign) AS Duration
FROM UAct
GROUP BY UserID
HAVING sum(Sign) > 0

┌──────────────UserID─┬─PageViews─┬─Duration─┐
│ 4324182021466249494 │         6 │      185 │
└─────────────────────┴───────────┴──────────┘
注記

上記の更新アプローチは、ユーザーがクライアント側で状態を維持する必要があります。 これは ClickHouse の観点からは最も効率的ですが、大規模での取り扱いは複雑になる可能性があります。

CollapsingMergeTree に関する文書を読むことをお勧めします より包括的な概要について。

追加リソース