OceanBaseにおけるストレージ形式
OceanBaseはLSM-Treeアーキテクチャを採用しており、ユーザーデータは全体としてベースラインデータと増分データの2つに分かれています。
- ベースラインデータ:テナントが定期的またはオンデマンドで選択したグローバルバージョン番号に基づき、メジャーコンパクション後に生成されます。同一バージョン下のすべてのレプリカのベースラインデータは物理的に完全に一致します。テーブル作成時に指定されたストレージモードに応じて、ベースラインデータは行ストア、列ストア、行列混合の3つの形態を取り得ます。
- 増分データ:最新のベースライン以降のすべての書き込み(MemTableメモリデータまたはフラッシュされたSSTable)は増分データに属し、各レプリカが独立して管理し、マルチバージョンデータを含みます。増分データは常に行ストアです。
上記のベースライン/増分データに基づき、行ストア、列ストア、行列混合は、ベースラインの編成方法、書き込みとマージのパス、およびクエリ時のアクセス特性においてそれぞれ異なります。次の表は、ストレージと書き込み特性、クエリ特性、典型的なシナリオ、推奨方法などの観点から3つの形式を比較し、業務負荷に応じた選定を容易にするものです。
ストレージ形式 |
ストレージと書き込み特性 |
クエリ特性 |
典型的なシナリオ |
推奨方法と説明 |
|---|---|---|---|---|
| 行ストア | ベースラインは行ストアで、すべての列はマイクロブロック内にまとめて保存されます。増分データは行ストアです。行ストアMemTableへの書き込みのみ / フラッシュ | ポイントクエリと高並行性TPに適しています | 純粋OLTP、ポイントクエリが主で、強力な分析要件がない場合 | デフォルトでテーブル作成は行ストアです。テナントのデフォルトが列ストアまたは混合に変更されている場合は、WITH COLUMN GROUP(all columns) を明示的に使用して行ストアテーブルを作成する必要があります |
| 列ストア | ベースラインは列ストアです:各列は独立したSSTableで、複数の列が仮想SSTableを形成します。増分データは書き込み時およびフラッシュ時には引き続き行ストアパスで処理されます。行ストアMemTableへの書き込みのみ / フラッシュ、マージ時にベースラインの列ストアと統合して新しい列ストアベースラインを生成します | 純粋列ストアテーブルのポイントクエリは通常行ストアの利点がなく、行ストアインデックスによる最適化が可能です。分析型クエリでは関連する列のみをスキャンするため、明らかな利点があります | OLAP分析が主です | テーブル作成時に WITH COLUMN GROUP(each column) を使用するか、テナントのdefault_table_store_formatをcolumnに設定します。行ストアベーステーブル + 列ストアインデックスなどの組み合わせについては、列ストアテーブルと列ストアインデックスの実践を参照してください。 |
| 行列混合 | 行ストアベースラインと列ストアベースラインが同時に存在します(冗長な2つ)。増分データは行ストアです。行ストアMemTableへの書き込みのみ / フラッシュ、マージ時には行ストアと列ストアのベースラインを同時にメンテナンスします | ポイントクエリは行ストアレプリカを使用します。範囲スキャンは通常列ストアパスの方が適していますが、最終的にはオプティマイザーがコストに基づいて選択します | HTAP:同一のテーブルがポイントクエリアクセスと分析型クエリの両方を同時にサービスする必要があり、統一されたストレージ構成で両方の負荷を兼ね備えたい場合 | WITH COLUMN GROUP(all columns, each column)を使用するか、テナントのデフォルト形式をcompoundに設定します。範囲スキャンはデフォルトで列ストアを経由し、ポイントクエリは行ストアに戻ります。コストは2つのベースラインとより高いストレージおよびマージリソースの消費です。実行計画のパスは統計情報とHintによるチューニングを組み合わせることができます |
データ量のみに基づいて列ストアを有効にするべきではない場合:データ量が多いが依然として高並行性のポイントクエリや短トランザクションの更新が主であり、大規模な範囲スキャンや集計の要件が欠けている場合、デフォルトの行ストアの方が通常適しています。無理に純粋列ストアテーブルを使用すると、ポイントクエリのパスは通常優れておらず、行ストアインデックスなどの設計に追加で依存する必要があります。
列ストアの全体的なアーキテクチャとコア特性については、列ストアを参照してください。
OceanBaseはデフォルトでテーブルを行ストアで作成します。AP業務で列ストアまたは行列混合が必要な場合は、テーブル作成ステートメント内の WITH COLUMN GROUP、テナントレベルのdefault_table_store_format、またはテーブル作成後に ALTER TABLE を使用して列グループを追加・削除することで変換を完了できます。
カラムストアテーブルと行・列混合テーブルの作成方法
方法1:テーブル作成時にストレージ形式を指定する
CREATE TABLE の WITH COLUMN GROUP を使用して、ストレージ形式を明示的に指定します:
ストレージ形式 |
WITH COLUMN GROUPの書き方 |
|---|---|
| 行ストア | WITH COLUMN GROUP(all columns) |
| カラムストア | WITH COLUMN GROUP(each column) |
| 行・列混合 | WITH COLUMN GROUP(all columns, each column) |
テナントのdefault_table_store_formatがrow以外の場合、行ストアテーブルを作成するには、明示的にWITH COLUMN GROUP(all columns)を記述する必要があります。
方法2:テナント構成パラメータでデフォルトストレージ形式を設定する
テナントレベルの構成パラメータ default_table_store_formatを使用すると、WITH COLUMN GROUPを指定しないテーブル作成ステートメントに対するデフォルト形式を指定できます:
row(デフォルト):デフォルトで行ストアテーブルを作成します。column:テーブル作成時に自動的にWITH COLUMN GROUP(each column)が追加され、デフォルトで純粋なカラムストアテーブルが作成されます。compound:テーブル作成時に自動的にWITH COLUMN GROUP(all columns, each column)が追加され、デフォルトで行・列混合テーブルが作成されます。
例:
-- テナント内のテーブルのデフォルトストレージ形式をカラムストアに設定する
ALTER SYSTEM SET default_table_store_format = "column";
-- テナント内のテーブルのデフォルトストレージ形式を行・列混合に設定する
ALTER SYSTEM SET default_table_store_format = "compound";
この構成パラメータは、テーブル作成時に明示的にWITH COLUMN GROUPを指定しなかったステートメントにのみ適用され、インデックステーブルには適用されません。
行ストアとカラムストア間の変換
テーブル作成後、ALTER TABLE を使用して列グループを追加・削除することで形式変換が可能です(構文については テーブルの変更(MySQLモード)を参照):
-- 例:行ストアの決済明細テーブルを純粋なカラムストアに変換する
CREATE TABLE payment_ledger (
txn_id BIGINT NOT NULL,
order_id BIGINT NOT NULL,
pay_time DATETIME NOT NULL,
amount DECIMAL(18,2) NOT NULL,
PRIMARY KEY (txn_id, pay_time)
) WITH COLUMN GROUP(all columns);
ALTER TABLE payment_ledger ADD COLUMN GROUP(each column);
ALTER TABLE payment_ledger DROP COLUMN GROUP(all columns);
-- 純粋なカラムストア -> 行と列のハイブリッドストア
ALTER TABLE payment_ledger ADD COLUMN GROUP(all columns);
変換後または一括ロード後は、メジャーコンパクションを実行し統計情報を収集した上で、EXPLAIN を使用して分析クエリがカラムストア経路を通るかどうか確認することを推奨します。
例
以下では、決済明細業務を例に、選定からテーブル作成、検証までの完全なプロセスを説明します。
シナリオ分析
同じ業務フィールド(注文番号、支払時間、チャネル、状態、金額など)を用いて、3種類のテーブル作成方法を比較し、WITH COLUMN GROUP の違いを理解しやすくします。
業務特性 |
推奨形式 |
|---|---|
| 日別・チャネル別の金額集計や成功率統計などの広範囲スキャン + 集計が主で、注文番号による単一レコードのクエリはほとんどない場合 | 純粋なカラムストアテーブル WITH COLUMN GROUP(each column) |
order_id による高頻度の個別取引明細のクエリと、テーブル全体に対する分析レポートの両方が必要な場合 |
行と列のハイブリッドストアテーブル WITH COLUMN GROUP(all columns, each column) |
| 単一支払いの書き込みや状態更新が主で、分析ニーズが低い場合 | 行ストアテーブル(デフォルトまたは明示的に WITH COLUMN GROUP(all columns)) |
テーブル作成
純粋なカラムストアテーブル
ベースラインは列単位でのみ構成され、少数の列をスキャンする集計系SQL(例:チャネル別の支払金額の集計)に適しています:
CREATE TABLE payment_ledger_cs (
txn_id BIGINT NOT NULL COMMENT '取引番号',
order_id BIGINT NOT NULL COMMENT '注文番号',
user_id BIGINT NOT NULL,
pay_time DATETIME NOT NULL,
pay_channel VARCHAR(32) NOT NULL COMMENT '支払チャネル',
txn_status TINYINT NOT NULL COMMENT '0未払 1成功 2失敗',
amount DECIMAL(18,2) NOT NULL,
merchant_id BIGINT NOT NULL
)
PARTITION BY RANGE COLUMNS(pay_time) (
PARTITION p202501 VALUES LESS THAN ('2025-02-01 00:00:00'),
PARTITION p202502 VALUES LESS THAN ('2025-03-01 00:00:00')
)
WITH COLUMN GROUP(each column);
WITH COLUMN GROUP(each column) は、ベースラインが列単位で保存されることを意味します。クエリで pay_channel、amount などの少数の列のみを読み取る場合、I/Oとベクトル化実行が有利になります。
クエリ(インポート完了後、メジャーコンパクションと統計情報収集を終えた後):
-- チャネル別に当日の決済成功金額を集計する
SELECT pay_channel, SUM(amount) AS total_amt, COUNT(*) AS cnt
FROM payment_ledger_cs
WHERE pay_time >= '2025-01-15 00:00:00'
AND pay_time < '2025-01-16 00:00:00'
AND txn_status = 1
GROUP BY pay_channel;
業務上、たまに order_id でのポイントクエリが必要な場合、純粋なカラムストアテーブルは通常優れているわけではありません。その場合は、行ストアインデックスを別途作成することができます。詳細については、カラムストアテーブルとカラムストアインデックスの実践を参照してください。
行と列のハイブリッドテーブル
ベースラインでは、行ストアとカラムストアの2つの構成を同時に保持し、同一のテーブルでポイントクエリと分析の両方に対応します:
CREATE TABLE payment_ledger_htap (
txn_id BIGINT NOT NULL,
order_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
pay_time DATETIME NOT NULL,
pay_channel VARCHAR(32) NOT NULL,
txn_status TINYINT NOT NULL,
amount DECIMAL(18,2) NOT NULL,
merchant_id BIGINT NOT NULL,
PRIMARY KEY (order_id, pay_time, txn_id)
)
PARTITION BY HASH(order_id) PARTITIONS 64
WITH COLUMN GROUP(all columns, each column);
WITH COLUMN GROUP(all columns, each column) は、行ストアの列グループとカラムストアの列グループを同時に宣言し、ベースラインで2つの冗長な構成を提供します。オプティマイザーはコストを基に、ポイントクエリでは行ストア経路を、広範囲スキャンではカラムストア経路を選択できます。
クエリ:
-- TP:注文番号で最後の決済成功件を照会する(行ストア経路を優先)
SELECT txn_id, pay_time, amount, pay_channel
FROM payment_ledger_htap
WHERE order_id = 10086001 AND txn_status = 1
ORDER BY pay_time DESC
LIMIT 1;
-- AP:過去7日間の店舗別支払総額を集計する(カラムストア経路を優先)
SELECT merchant_id, DATE(pay_time) AS dt, SUM(amount) AS daily_amt
FROM payment_ledger_htap
WHERE pay_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND txn_status = 1
GROUP BY merchant_id, DATE(pay_time);
検証
サンプルデータを書き込み、メジャーコンパクションと統計情報収集を完了した後、分析系SQLに対して EXPLAIN を実行し、COLUMN TABLE FULL SCAN などのカラムストア関連演算子が出現するかどうか確認できます(現在のバージョンの実行計画出力を基準とします):
EXPLAIN SELECT pay_channel, SUM(amount) FROM payment_ledger_cs
WHERE pay_time >= '2025-01-01' GROUP BY pay_channel;
カラムストアテーブルの使用時の注意事項
- メジャーコンパクションと統計情報:一括インポートまたは大規模データの準備が整った後、メジャーコンパクションを実行し統計情報を収集することを推奨します。これにより、読み取り性能が向上し、オプティマイザーが有効な実行計画を生成しやすくなります。カラムストアテーブルのデータ量が多い場合、メジャーコンパクションにかかる時間は行ストアに比べて相対的に長くなる可能性があります。
- 更新とメジャーコンパクション:カラムストアテーブルに大量の更新が存在し、タイムリーにメジャーコンパクションが行われていない場合、クエリ性能に影響が出ます。一括インポート後に一度メジャーコンパクションを行うことで、より優れたクエリ性能を得られます。
- カラムストアとカラムストアレプリカ:本記事で説明しているカラムストアテーブルとは、テーブルがF/Rレプリカ上に保存される形式を指します。独立したカラムストアレプリカ(Cレプリカ)をAPに使用する場合は、APデプロイメントの概要を参照してください。