データ更新概要
今日のデータ駆動型意思決定の世界において、データの「鮮度」は、企業が激しい市場競争で差別化を図るための核となる競争優位性となっています。従来のT+1データ処理モデルは、その固有の遅延により、現代ビジネスの厳しいリアルタイム要件をもはや満たすことができません。ビジネスデータベースとデータウェアハウス間でのミリ秒レベルの同期の実現、運用戦略の動的調整、または意思決定の精度を確保するための数秒以内での誤ったデータの修正など、堅牢なリアルタイムデータ更新機能が重要です。
Apache Dorisは、現代のリアルタイム分析データベースとして、究極のデータ鮮度を提供することを核となる設計目標の一つとしています。その強力なデータモデルと柔軟な更新メカニズムにより、データ分析の遅延を日レベル、時間レベルから秒レベルまで短縮することに成功し、ユーザーがリアルタイムで機敏なビジネス意思決定ループを構築するための堅実な基盤を提供しています。
本ドキュメントは、Apache Dorisのデータ更新機能を体系的に説明する公式ガイドとして機能し、その核となる原理、多様な更新および削除方法、典型的な応用シナリオ、および異なるデプロイモードでのパフォーマンスベストプラクティスをカバーし、Dorisのデータ更新機能を包括的に習得し効率的に活用できるよう支援することを目的としています。
1. 核となる概念:テーブルモデルと更新メカニズム
Dorisにおいて、データテーブルのData Modelは、そのデータ構成と更新動作を決定します。異なるビジネスシナリオをサポートするため、DorisはUnique Key Model、Aggregate Key Model、Duplicate Key Modelという3つのテーブルモデルを提供しています。この中で、Unique Key Modelは複雑で高頻度なデータ更新を実装するための核となります。
1.1. テーブルモデル概要
| table Model | 主要機能 | 更新機能 | 使用ケース |
|---|---|---|---|
| Unique Key Model | リアルタイム更新用に構築。各データ行は一意のPrimary Keyによって識別され、行レベルのUPSERT(アップデート/Insert)と部分列更新をサポート。 | 最強、すべての更新および削除方法をサポート。 | 注文ステータス更新、リアルタイムユーザータグ計算、CDCデータ同期、その他頻繁でリアルタイムな変更が必要なシナリオ。 |
| Aggregate Key Model | 指定されたKey列に基づいてデータを事前集約。同じKeyを持つ行について、Value列は定義された集約関数(SUM、MAX、MIN、REPLACEなど)に従ってマージされる。 | 限定的、Key列に基づくREPLACEスタイルの更新と削除をサポート。 | リアルタイムサマリー統計が必要なシナリオ(リアルタイムレポート、広告クリック統計など)。 |
| Duplicate Key Model | データは追記のみの書き込みをサポートし、重複排除や集約操作は行わない。同一のデータ行でも保持される。 | 限定的、DELETE文による条件付き削除のみサポート。 | ログ収集、ユーザー行動追跡、その他更新なしで追記のみが必要なシナリオ。 |
1.2. データ更新方法
Dorisは2つの主要なデータ更新方法カテゴリを提供します:データロードによる更新とDML文による更新。
1.2.1. Load による更新(UPSERT)
これはDorisの推奨される高パフォーマンス、高並行性更新方法で、主にUnique Key Modelを対象としています。すべてのロード方法(Stream Load、Broker Load、Routine Load、INSERT INTO)は自然にUPSERTセマンティクスをサポートします。新しいデータがロードされる際、そのprimary keyが既に存在する場合、Dorisは古い行データを新しい行データで上書きし、primary keyが存在しない場合は新しい行を挿入します。

1.2.2. UPDATE DML文による更新
Dorisは標準SQL UPDATE文をサポートし、ユーザーがWHERE句で指定された条件に基づいてデータを更新できます。この方法は非常に柔軟で、テーブル間結合更新などの複雑な更新ロジックをサポートします。

-- Simple update
UPDATE user_profiles SET age = age + 1 WHERE user_id = 1;
-- Cross-table join update
UPDATE sales_records t1
SET t1.user_name = t2.name
FROM user_profiles t2
WHERE t1.user_id = t2.user_id;
注意: UPDATE文の実行プロセスは、まず条件に合致するデータをスキャンし、その後更新されたデータをテーブルに書き戻すことを含みます。これは低頻度のバッチ更新タスクに適しています。UPDATE文の高並行性操作は推奨されません。同じプライマリキーが関わる並行UPDATE操作では、データの分離が保証できないためです。
1.2.3. INSERT INTO SELECT DML文による更新
DorisはデフォルトでUPSERTセマンティクスを提供するため、INSERT INTO SELECTを使用することでUPDATEと同様の更新効果を実現することもできます。
1.3. データ削除方法
更新と同様に、Dorisはロードとdml文の両方によるデータ削除もサポートしています。
1.3.1. ロードによるマーク削除
これは効率的なバッチ削除方法で、主にUnique Key Modelで使用されます。ユーザーはデータロード時に特別な隠しカラムDORIS_DELETE_SIGNを追加できます。ある行のこのカラムの値が1またはtrueの場合、Dorisはそのプライマリキーを持つ対応するデータ行を削除済みとしてマークします(delete signの原理については後で詳細に説明します)。
// Stream Load load data, delete row with user_id = 2
// curl --location-trusted -u user:passwd -H "columns:user_id, __DORIS_DELETE_SIGN__" -T delete.json http://fe_host:8030/api/db_name/table_name/_stream_load
// delete.json content
[
{"user_id": 2, "__DORIS_DELETE_SIGN__": "1"}
]
1.3.2. DELETE DML文による削除
Dorisは標準SQL DELETE文をサポートしており、WHERE条件に基づいてデータを削除できます。
- Unique Key Model:
DELETE文は条件に合致する行のprimary keyを削除マークで書き換えます。そのため、パフォーマンスは削除対象データ量に比例します。Unique Key ModelにおけるDELETE文の実行原理はUPDATE文と非常に似ており、まずクエリを通じて削除対象データを読み取り、その後削除マークを付けて再度書き込みます。UPDATE文と比較して、DELETE文はKey列と削除マーク列のみを書き込めばよいため、比較的軽量です。 - Duplicate/Aggregate Models:
DELETE文はdelete predicateを記録することで実装されます。クエリ時に、このpredicateはランタイムフィルターとして機能し、削除されたデータをフィルタリングします。そのため、DELETE操作自体は非常に高速で、削除データ量にほぼ依存しません。ただし、Duplicate/Aggregate Modelに対する高頻度のDELETE操作は多数のランタイムフィルターを蓄積し、後続のクエリパフォーマンスに深刻な影響を与えることに注意してください。
DELETE FROM user_profiles WHERE last_login < '2022-01-01';
以下の表は、削除におけるDML文の使用について簡潔にまとめたものです:
| Unique Key Model | Aggregate Model | Duplicate Model | |
|---|---|---|---|
| 実装方式 | Delete Sign | Delete Predicate | Delete Predicate |
| 制限事項 | なし | Key列のみ削除条件指定可能 | なし |
| 削除性能 | 中程度 | 高速 | 高速 |
2. Unique Key Modelの詳細解説:原理と実装
Unique Key ModelはDorisの高性能リアルタイム更新の中核です。その内部動作原理を理解することは、性能を最大限に活用するために不可欠です。
2.1. Merge-on-Write (MoW) vs. Merge-on-Read (MoR)
Unique Key Modelには2つのデータマージ戦略があります:Merge-on-Write (MoW)とMerge-on-Read (MoR)です。Doris 2.1以降、MoWがデフォルトかつ推奨実装となっています。
| 機能 | Merge-on-Write (MoW) | Merge-on-Read (MoR) - (Legacy) |
|---|---|---|
| 基本概念 | データ書き込み時にデータ重複排除とマージを完了し、ストレージ内で主キーごとに最新レコードのみを保証する。 | データ書き込み時に複数バージョンを保持し、クエリ時にリアルタイムマージを実行して最新バージョンを返す。 |
| クエリ性能 | 極めて高い。クエリ時に追加のマージ操作が不要で、性能は更新されていない詳細テーブルに近い。 | 低い。クエリ時にデータマージが必要で、MoWの約3-10倍の時間がかかり、より多くのCPUとメモリを消費する。 |
| 書き込み性能 | 書き込み時にマージオーバーヘッドがあり、MoRと比較して性能低下がある(小バッチで約10-20%、大バッチで30-50%)。 | 書き込み速度が高速で、詳細テーブルに近い。 |
| リソース消費 | 書き込み時およびバックグラウンドCompaction時により多くのCPUとメモリを消費する。 | クエリ時により多くのCPUとメモリを消費する。 |
| 用途 | ほとんどのリアルタイム更新シナリオ。特に読み取り重視、書き込み軽量のビジネスに適しており、究極のクエリ分析性能を提供する。 | 書き込み重視、読み取り軽量のシナリオに適しているが、もはや主流推奨ではない。 |
MoWメカニズムは書き込みフェーズでの小さなコストをクエリ性能の大幅な向上と引き換えにしており、OLAPシステムの「読み取り重視、書き込み軽量」という特性に完全に合致しています。
2.2. 条件付き更新(Sequence Column)
分散システムでは、データの順序外到着は一般的な問題です。例えば、注文ステータスが「支払済み」「発送済み」と順次変更される際、ネットワーク遅延により「発送済み」を表すデータが「支払済み」を表すデータよりも先にDorisに到着する可能性があります。
この問題を解決するため、DorisはSequence Columnメカニズムを導入しています。ユーザーはテーブル作成時に列(通常はタイムスタンプまたはバージョン番号)をSequence列として指定できます。同一主キーのデータを処理する際、DorisはそれらのSequence列の値を比較し、常に最大のSequence値を持つ行を保持することで、データが順序外で到着してもeventual consistencyを保証します。
CREATE TABLE order_status (
order_id BIGINT,
status_name STRING,
update_time DATETIME
)
UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id)
PROPERTIES (
"function_column.sequence_col" = "update_time" -- Specify update_time as Sequence column
);
-- 1. Write "Shipped" record (larger update_time)
-- {"order_id": 1001, "status_name": "Shipped", "update_time": "2023-10-26 12:00:00"}
-- 2. Write "Paid" record (smaller update_time, arrives later)
-- {"order_id": 1001, "status_name": "Paid", "update_time": "2023-10-26 11:00:00"}
-- Final query result, retains record with largest update_time
-- order_id: 1001, status_name: "Shipped", update_time: "2023-10-26 12:00:00"
2.3. 削除メカニズム(DORIS_DELETE_SIGN)のワークフロー
DORIS_DELETE_SIGNの動作原理は「論理マーキング、バックグラウンドクリーンアップ」として要約できます。
- 削除の実行: ユーザーがloadまたは
DELETE文を通じてデータを削除する際、Dorisは物理ファイルからデータを即座に削除しません。代わりに、削除対象のprimary keyに対して新しいレコードを書き込み、DORIS_DELETE_SIGNカラムを1としてマークします。 - クエリフィルタリング: ユーザーがデータをクエリする際、Dorisは自動的にクエリプランに
WHERE DORIS_DELETE_SIGN = 0のフィルタ条件を追加し、削除マークされたすべてのデータをクエリ結果から隠します。 - バックグラウンドCompaction: DorisのバックグラウンドCompactionプロセスは定期的にデータをスキャンします。通常のレコードと削除マークレコードの両方を持つprimary keyを見つけると、マージプロセス中に両方のレコードを物理的に削除し、最終的にストレージスペースを解放します。
このメカニズムは削除操作への迅速な応答を保証しながら、バックグラウンドタスクを通じて物理的クリーンアップを非同期で完了し、オンラインビジネスへのパフォーマンス影響を回避します。
以下の図はDORIS_DELETE_SIGNの動作を示しています:

2.4 部分カラム更新
バージョン2.0から、DorisはUnique Key Models(MoW)で強力な部分カラム更新機能をサポートしています。データをロードする際、ユーザーはprimary keyと更新対象のカラムのみを提供すれば良く、提供されないカラムは元の値を変更せずに維持します。これにより、ワイドテーブル結合やリアルタイムタグ更新などのシナリオでのETLプロセスが大幅に簡素化されます。
この機能を有効にするには、Unique Key Modelテーブルを作成する際にMerge-on-Write(MoW)モードを有効にする必要があります。INSERT INTOの場合は、セッション変数enable_unique_key_partial_updateをtrueに設定して部分カラム更新を有効にします。Stream Loadやその他のimport方法の場合は、partial_columnsパラメータを設定して部分カラム更新を有効にします。
CREATE TABLE user_profiles (
user_id BIGINT,
name STRING,
age INT,
last_login DATETIME
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true"
);
-- Initial data
-- user_id: 1, name: 'Alice', age: 30, last_login: '2023-10-01 10:00:00'
-- load partial update data through Stream Load, only updating age and last_login
-- {"user_id": 1, "age": 31, "last_login": "2023-10-26 18:00:00"}
-- Updated data
-- user_id: 1, name: 'Alice', age: 31, last_login: '2023-10-26 18:00:00'
部分列更新原理概要
従来のOLTPデータベースとは異なり、Dorisの部分列更新はインプレースデータ更新ではありません。Dorisでより良い書き込みスループットとクエリパフォーマンスを実現するため、Unique Key Modelの部分列更新は「ロード時の欠損フィールド補完とその後の全行書き込み」という実装アプローチを採用しています。
したがって、Dorisの部分列更新を使用すると**「読み取り増幅」と「書き込み増幅」**効果があります。例えば、100列の幅広いテーブルで10フィールドを更新する場合、Dorisは書き込みプロセス中に欠損している90フィールドを補完する必要があります。各フィールドのサイズが同程度と仮定すると、1MBの10フィールド更新により、Dorisシステムでは約9MBのデータ読み取り(欠損フィールドの補完)と10MBのデータ書き込み(新しいファイルへの完全な行の書き込み)が発生し、約9倍の読み取り増幅と10倍の書き込み増幅が生じます。
部分列更新パフォーマンス推奨事項
部分列更新における読み取りと書き込み増幅のため、そしてDorisが列指向ストレージシステムであるため、データ読み取りプロセスは大量のランダムI/Oを生成する可能性があり、ストレージからの高いランダム読み取りIOPSが必要です。従来の機械式ディスクはランダムI/Oに大きなボトルネックがあるため、高頻度書き込みで部分列更新機能を使用したい場合は、SSDドライブ、できればNVMeインターフェースを推奨します。これにより最適なランダムI/Oサポートが提供されます。
また、テーブルが非常に幅広い場合、ランダムI/Oを削減するために行ストレージを有効にすることも推奨されます。行ストレージを有効にすると、Dorisは列指向ストレージと併せて行ベースデータの追加コピーを保存します。行ベースデータは各行を連続的に保存するため、単一のI/O操作で行全体を読み取ることができます(列指向ストレージでは、すべての欠損フィールドを読み取るためにN回のI/O操作が必要です。例えば、前述の100列の幅広いテーブルで10列を更新する場合、すべてのフィールドを読み取るために行ごとに90回のI/O操作が必要です)。
3. 典型的なアプリケーションシナリオ
Dorisの強力なデータ更新機能により、様々な要求の厳しいリアルタイム分析シナリオに対応できます。
3.1. CDCリアルタイムデータ同期
Flink CDCなどのツールを通じて上流のビジネスデータベース(MySQL、PostgreSQL、Oracleなど)から変更データ(Binlog)をキャプチャし、DorisのUnique Key Modelテーブルにリアルタイムで書き込むことは、リアルタイムデータウェアハウス構築の最も古典的なシナリオです。
- 全データベース同期: Flink Doris ConnectorはFlink CDCを内部統合しており、手動のテーブル作成やフィールドマッピング設定なしに、上流データベースからDorisへの自動化されたエンドツーエンドの全データベース同期を可能にします。
- 整合性の確保: Unique Key Modelの
UPSERT機能を利用して上流のINSERTおよびUPDATE操作を処理し、DORIS_DELETE_SIGNを使用してDELETE操作を処理し、Sequenceカラム(Binlogのタイムスタンプなど)と組み合わせて順序不整合データを処理することで、上流データベースの状態を完全に複製し、ミリ秒レベルのデータ同期遅延を実現します。

3.2. リアルタイム幅広テーブル結合
多くの分析シナリオでは、異なるビジネスシステムのデータをユーザー幅広テーブルや製品幅広テーブルに結合する必要があります。従来のアプローチでは、オフラインETLタスク(SparkやHiveなど)を使用して定期的(T+1)な結合を行いますが、これはリアルタイム性能が低く、メンテナンスコストが高くなります。また、Flinkを使用してリアルタイム幅広テーブル結合計算を行い、結合データをデータベースに書き込む場合は、通常大量の計算リソースが必要です。
Dorisの部分列更新機能を使用することで、このプロセスを大幅に簡素化できます:
- DorisでUnique Key Model幅広テーブルを作成します。
- 異なるソース(ユーザー基本情報、ユーザー行動データ、取引データなど)からのデータストリームをStream LoadまたはRoutine Load経由でこの幅広テーブルにリアルタイムで書き込みます。
- 各データストリームは関連するフィールドのみを更新します。例えば、ユーザー行動データストリームは
page_view_count、last_login_timeなどのフィールドのみを更新し、取引データストリームはtotal_orders、total_amountなどのフィールドのみを更新します。
このアプローチにより、幅広テーブル構築をオフラインETLからリアルタイムストリーム処理に変換し、データの新鮮性を大幅に向上させるだけでなく、変更された列のみを書き込むことでI/Oオーバーヘッドを削減し、書き込みパフォーマンスを向上させます。
4. ベストプラクティス
これらのベストプラクティスに従うことで、Dorisのデータ更新機能をより安定的かつ効率的に使用できます。
4.1. 一般的なパフォーマンスプラクティス
- loadによる更新を優先: 高頻度、大量の更新操作については、
UPDATEDMLステートメントよりもStream LoadやRoutine Loadなどのloadメソッドを優先してください。 - バッチ書き込み: 個別の高頻度書き込み(> 100 TPSなど)で
INSERT INTOステートメントの使用を避けてください。各INSERTにはトランザクションオーバーヘッドが発生します。必要に応じて、複数の小さなバッチコミットを1つの大きなトランザクションにマージするGroup Commit機能を有効にすることを検討してください。 - 高頻度
DELETEの慎重な使用: Duplicate KeyやAggregateモデルでは、クエリパフォーマンス低下を防ぐため、高頻度のDELETE操作を避けてください。 - パーティションデータ削除には
TRUNCATE PARTITIONを使用: パーティション全体のデータを削除する必要がある場合は、DELETEよりもはるかに効率的なTRUNCATE PARTITIONを使用してください。 UPDATEの直列実行: 同じデータ行に影響を与える可能性のあるUPDATEタスクの並行実行を避けてください。
4.2. コンピューティング・ストレージ分離アーキテクチャにおけるUnique Key Modelプラクティス
Doris 3.0は先進的なコンピューティング・ストレージ分離アーキテクチャを導入し、究極の弾力性とより低いコストをもたらします。このアーキテクチャでは、BEノードがステートレスであるため、Merge-on-Writeプロセス中にload/compaction/schema change操作間の書き込み-書き込み競合を解決するため、MetaServiceを通じてグローバル状態を維持する必要があります。Unique Key ModelのMoW実装は、書き込み操作の整合性を確保するため、Meta Serviceベースの分散テーブルロックに依存します。以下の図に示すとおりです:

高頻度のloadとCompactionはテーブルロックの頻繁な競合を引き起こすため、以下の点に特に注意する必要があります:
- 単一テーブルのload頻度制御: 単一のUnique Key Modelテーブルのload頻度を60回/秒以内に制御することを推奨します。これはバッチ処理とload並行性の調整により実現できます。
- 合理的なパーティションとバケット設計:
- パーティション: 時間パーティション(日や時間単位など)を使用することで、単一のloadが少数のパーティションのみを更新し、ロック競合の範囲を削減します。
- バケット: バケット数(Tabletカウント)はデータ量に基づいて合理的に設定し、通常8-64の間にします。Tabletが多すぎるとロック競合が激化します。
- Compaction戦略の調整: 非常に高い書き込み圧力のシナリオでは、Compaction戦略を適切に調整してCompaction頻度を削減し、Compactionとloadタスク間のロック競合を減らすことができます。
- 最新バージョンへのアップグレード: Dorisコミュニティはコンピューティング・ストレージ分離アーキテクチャでのUnique Key Modelパフォーマンスを継続的に最適化しています。例えば、間もなくリリースされる3.1では分散テーブルロック実装が大幅に最適化されます。最適なパフォーマンスのため、常に最新の安定バージョンの使用を推奨します。
結論
Apache Dorisは、Unique Key Modelを中心とした強力で柔軟かつ効率的なデータ更新機能により、従来のOLAPシステムのデータ新鮮性におけるボトルネックを真に突破します。UPSERTと部分列更新を実装する高性能loadや、順序不整合データの整合性を確保するSequenceカラムの使用など、Dorisはエンドツーエンドのリアルタイム分析アプリケーション構築のための完全なソリューションを提供します。
その核心原理を深く理解し、異なる更新方法の適用可能なシナリオを習得し、この文書で提供されるベストプラクティスに従うことで、Dorisの潜在能力を完全に発揮し、リアルタイムデータを真にビジネス成長を推進する強力なエンジンにすることができるでしょう。