OceanBaseデータベースにおいて、テーブルは最も基本的なデータ格納単位です。テーブルにはすべてのユーザーがアクセス可能なデータが含まれており、各テーブルは複数の行で構成され、各行は複数の列で構成されます。各テーブルの設計と使用は、業務要件に基づいて適切に計画する必要があり、システムの効率性と拡張性を確保するために不可欠です。
本記事では、テーブルタイプ、更新モード、データ分布、インデックス、データ型、ビューなどの観点から解説し、APテーブル設計原則を中心に、シナリオごとの推奨事項と組み合わせの説明を提供します。
APシナリオにおけるテーブル設計の原則
AP(分析処理)シナリオにおけるテーブル設計は、以下の3つの原則を中心に展開し、後述のテーブルタイプ、データ分布、インデックス、ビューなどの機能と組み合わせて使用します:
- ワイドテーブル:分析シナリオでは、列数が多いワイドテーブルがよく使用され、単一クエリで部分的な列のみにアクセスします。カラムストアテーブルまたはカラムストアインデックスを採用することで、列単位での格納とスキャンが可能になり、I/Oが削減され、ベクトル化やSkip Indexが容易になります。さらに、パーティションやテーブルグループと組み合わせることで、ワイドテーブルを水平方向に分散させ、他のテーブルと整列させることができ、Joinや並列スキャンを容易にします。詳細については、本記事の「テーブルタイプ」「データ分布」およびカラムストアと行列混合の使用を参照してください。
- 事前集計:固定次元の集計やレポート系のクエリでは、マテリアライズドビューなどを用いて事前に結果を計算・永続化し、クエリ時にはマテリアライズドビューから直接読み取ることで、リアルタイム集計のオーバーヘッドを低減します。レポート、ダッシュボード、固定の分析基準などのシナリオに適しています。詳細については、本記事の「ビュー」およびマテリアライズドビュー(MySQLモード)、マテリアライズドビュー(Oracleモード)を参照してください。
- パーティション戦略:パーティションテーブルを使用し、パーティションキー(時間、地域、IDなど)に基づいてデータを水平方向に分割することで、パーティションプルーニングによるスキャン量の削減、パーティションごとの並列計算が可能になり、パーティション単位でのライフサイクル管理(アーカイブ、クリーンアップ)も容易になります。APシナリオでは、パーティションキーを一般的なフィルタリング・集計次元と一致させることを推奨し、テーブルグループと組み合わせて複数テーブルのパーティションを整列させ、Joinを最適化します。詳細については、本記事の「データ分布」およびOLAPシナリオにおけるテーブルパーティション設計の実践を参照してください。
テーブルタイプ
OceanBaseデータベースでサポートされているテーブルタイプには、パーティションテーブル、レプリケーションテーブル、主キーテーブル、主キーのないテーブル、外部テーブルなどがあります。
- パーティションテーブル:OceanBaseデータベースでは、通常のテーブルのデータを一定のルールに従って異なるブロックに分割し、同一ブロックのデータを物理的にまとめて保存することができます。このようにブロックに分割されたテーブルをパーティションテーブルと呼びます。OceanBaseデータベースの基本的なパーティション戦略には、範囲(Range)パーティション、リスト(List)パーティション、ハッシュ(Hash)パーティション、Keyパーティションが含まれます。
- レプリケーションテーブル:レプリケーションテーブルは、OceanBaseデータベースにおける特殊なテーブルです。このテーブルでは、任意の「正常」なレプリカでデータの最新の変更を読み取ることができます。
- 主キーテーブルと主キーのないテーブル:主キーテーブルとは主キーを含むテーブルのことです。主キーが指定されていないテーブルは主キーのないテーブルと呼ばれます。
- 外部テーブル:データベース内のテーブルデータはデータベースのストレージ領域に保存されますが、外部テーブルのデータは外部ストレージサービスに保存されます。
- ヒープテーブル(ヒープ構造テーブル):データは主キー順にソートされずに保存され、主キーは二次的な一意のインデックスとなり、データとインデックスは分離されます。主キーは一意性制約に使用され、クエリは主テーブルに依存します。ユーザーデータが時間順に書き込まれる場合、Skip Indexを効果的に活用して分析型クエリの効率を向上させることができます。APシナリオでは、新規作成テーブルはデフォルトでヒープ構造テーブルとなります。テナントレベルのパラメータ default_table_organizationがOLAPパラメータテンプレートで
HEAPに設定されており、ORGANIZATIONが明示的に指定されていない場合、新規作成テーブルはヒープテーブルとなり、大量インポートやSkip Indexの利用に適しています。 - 一時テーブル:APシナリオでは、ETL中間結果の格納、ダイレクトロードの一時保存、複雑なクエリの中間結果キャッシュなどにも一時テーブルが頻繁に使用されます。データはセッションまたはトランザクション単位で分離され、セッション終了時やトランザクションコミット/ロールバック後に自動的にクリーンアップされるため、永続化テーブルとは区別されます。MySQLモードではセッション単位の一時テーブルが、Oracleモードではグローバルな一時テーブルがサポートされています。
APシナリオでは、OceanBaseは多様なテーブルタイプをサポートしています。TPシナリオで一般的な レプリケーションテーブル、パーティションテーブル、主キーテーブル、主キーのないテーブルに加え、データの保存方法、行単位か列単位かによって、新たな次元のテーブルタイプが導入されています:カラムストアテーブル、行列混合テーブル。
カラムストアテーブル:カラムストアテーブルはデータを行ではなく列単位で保存するため、分析型クエリのパフォーマンスを大幅に向上させることができ、特にデータ量が多く集計分析を頻繁に行うシナリオに適しています。詳細については、カラムストアテーブルのアーキテクチャを参照してください。
行列混合テーブル:行単位と列単位で保存されたデータがそれぞれ別々に保存されており、システムはクエリステートメントに基づいて、行ストアと列ストアのどちらでクエリを実行する方がパフォーマンスが優れているかを自動的に判断します。これは、トランザクション業務と分析業務の両方を扱うシナリオに適しています。
テーブル更新モード
OceanBaseデータベースでは、テーブル作成時にデータの書き込みモードとクエリモードを指定できます。テーブル作成時に CREATE TABLE ステートメントのパラメータ merge_engine を使用して、delete_insert 更新モード と partial_update 更新モード を選択します。これら2つのモードは、異なるビジネスシナリオに対応するために設計されたデータ更新戦略です。
delete_insert(全列更新モード)
クエリ性能を優先します。「Merge-On-Write」という仕組みにより、
UPDATE操作を全列のDELETEとINSERTレコードに変換し、各行のデータが完全な列値を含むことを保証します。このモードは、複雑なクエリやバッチ処理(分析タスクなど)の効率を大幅に向上させますが、増分データには追加のストレージ容量が必要となります。増分データが頻繁でかつ迅速な分析が必要な シナリオに適しています。partial_update(部分更新モード)
変更された列の値のみを記録し、冗長なストレージを回避します。クエリ時には複数のデータをマージして最新値を取得する必要があるため、パフォーマンスは相対的に低くなります。しかし、更新頻度が高いがクエリ要求が低い シナリオ(例:OLTP業務)や、ストレージコストを重視する環境に適しています。
特性カテゴリ |
delete_insert 更新モード |
partial_update 更新モード |
|---|---|---|
| ストレージ方式 | 更新のたびにSSTableに全列データを含む2行(DELETEとINSERT)を書き込む。 |
更新のたびに変更された列の値のみを記録するため、ストレージ容量を節約できる。 |
| クエリ効率 |
|
クエリ時には複数のMemtable/SSTableのレコードをマージして主キーの最新値を取得する必要があるため、パフォーマンスに影響を与える可能性がある。 ストレージコストを重視し、更新操作が頻繁なシナリオに適している。 |
| 適用シナリオ | 増分データの割合が高く、複雑なクエリやバッチ処理の分析を頻繁に実行する必要があるシナリオ。 | 更新頻度が高いがクエリ要求が低いシナリオ。 |
詳細については、MySQLモードでのテーブル作成およびOracleモードでのテーブル作成を参照してください。
データ分布
パーティション戦略は、APテーブル設計において重要な要素です。OceanBaseデータベースでは、パーティションテーブルを作成することでデータを異なるパーティションに分散し、異なるパーティションのデータを異なるマシンに配置することができます。クエリ時にはパーティションプルーニングを利用してデータのスキャン量を削減し、複数マシンのリソースを活用してクエリ性能を向上させることができます。デフォルトでは、異なるテーブル間のデータはランダムに分布しており、直接的な関連性はありません。ロードバランシングを利用することで、あるテーブルのデータをクラスタ全体に比較的均等に分散させることができます。
分散型APシステムでは、テーブルのデータ量は通常多くなります。異なるテーブルのデータがランダムに分布している場合、テーブル結合時のデータ転送オーバーヘッドはしばしば無視できません。テーブルグループを利用することで、同じパーティション方式を持つパーティションテーブルのデータを特定のルールに従って整列させ、関連性のあるデータを同一マシン上に集約することができます。これにより、これらのテーブルを結合する際にPartition Wise Join方式で実行でき、結合シナリオにおけるデータ転送のオーバーヘッドを効果的に低減し、性能を向上させることができます。
OceanBaseはパーティションテーブルとサブパーティションテーブルをサポートしており、RANGEパーティション、LISTパーティション、HASHパーティション、KEYパーティションの4種類のパーティションタイプをサポートしています。
OceanBaseは3種類のプロパティを持つテーブルグループを提供します:
NONE:テーブルグループ内のすべてのテーブルのすべてのパーティションが同一マシンに集約されます。
PARTITION:テーブルグループ内の各テーブルのデータはパーティション単位で分散されます。サブパーティションテーブルの場合、各パーティションの下にあるすべてのサブパーティションがまとめられます。
ADAPTIVE:テーブルグループ内の各テーブルのデータは適応型方式で分散されます。テーブルグループ内のテーブルがパーティションテーブルの場合はパーティション単位で、サブパーティションテーブルの場合は各パーティションの下のサブパーティション単位で分散されます。
詳細については、データ分散、MySQLモードのテーブルグループおよびOracleモードのテーブルグループを参照してください。
インデックスタイプ
インデックスはクエリパフォーマンスを向上させるための重要なコンポーネントです。OceanBaseはAPシナリオで多様なインデックスタイプをサポートしており、等値/範囲クエリ、全文検索、JSON集合クエリ、ベクトル類似性検索、分析型スキャンなどを高速化します。
- インデックスデータの分布:ローカルインデックスとは、インデックスパーティションがベーステーブルのパーティションと一対一で対応し、結合して格納されることを指します。グローバルインデックスとは、インデックスがベーステーブルのパーティションと独立しており、パーティションをまたいで構成できることを指します。これらは「インデックスがどのパーティションに作成され、どのように分布するか」と関連しています。
- インデックスのストレージ形式:行ストアインデックスとカラムストアインデックスは、インデックステーブル自体が行単位または列単位でデータを整理していることを指します。カラムストア形式(例:
WITH COLUMN GROUP (each column))は分析型スキャンや集計に適しており、行ストア形式はポイントクエリやランダムアクセスに適しています。カラムストアインデックスとは、カラムストアストレージ形式を採用したインデックスのことです(例:B-treeインデックスをカラムストアとして指定した場合)。インデックステーブルは列単位(WITH COLUMN GROUP (each column)など)でデータを整理し、行ではなく列に基づいて格納されます。これはHTAPシナリオでの集計、ワイドテーブルのスキャン、分析型クエリに適しており、特に大規模データを扱う際のデータ検索と分析時間を大幅に短縮できます。詳細については、カラムストアおよびインデックスの作成(MySQLモード)— カラムストアインデックスを参照してください。
上記の2つの観点から、インデックス構造に基づいて以下のカテゴリに分類できます:
B-treeインデックス(通常のインデックス):デフォルトのインデックス構造で、一意インデックス、非一意インデックス、関数インデックスが含まれます。等値クエリ、範囲クエリ、ソート、ポイントクエリに適しています。B-treeインデックスはローカルインデックスまたはグローバルインデックスになり得、行ストアまたはカラムストア形式を採用することもできます。カラムストア形式のB-treeインデックスは、一般的に言われるカラムストアインデックスであり、HTAPシナリオでの分析型スキャンに適しています。詳細については、インデックスの概要、インデックスの作成(MySQLモード)、カラムストアを参照してください。その中で、関数インデックスは式に基づくフィルタリングやソート(例:
MONTH(date_col)、UPPER(name))に適しており、行ごとの計算を回避します。詳細については、関数インデックスがサポートするシステム関数のリストおよびサポートされていない関数のリストを参照してください。- 適用シナリオ:等値/範囲条件(
WHERE col = ?、BETWEEN、IN);ソートとページネーション(ORDER BYとフィルタリングの組み合わせ);パーティションキーまたはよく使用されるフィルター列にローカルインデックスを作成し、パーティションプルーニングを補完する;主キー/一意キーまたはカバリングインデックスによる再テーブルアクセスの削減、これは属性テーブルの関連付け、小さな結果セットのフィルタリングなどに適しています。
- 適用シナリオ:等値/範囲条件(
全文インデックス:転置インデックスと分かち書きに基づき、
CHAR/VARCHAR/TEXT列に対してキーワードマッピングを構築します。ローカルインデックスのみをサポートします。詳細については、全文インデックスを参照してください。- 適用シナリオ:企業内のナレッジベースや文書検索、ニュース・法律文書などの長文検索、ログ分析におけるキーワードによる位置特定などに適しています。実行計画で
TEXT RETRIEVAL SCANが見られる場合、全文インデックスがヒットしたことを意味します。
- 適用シナリオ:企業内のナレッジベースや文書検索、ニュース・法律文書などの長文検索、ログ分析におけるキーワードによる位置特定などに適しています。実行計画で
JSON複数値インデックス:OceanBaseデータベースのMySQLモードは複数値インデックス機能をサポートしており、配列や集合にインデックスを作成して、JSON配列要素に基づく検索のクエリ効率を向上させることができます。ローカルインデックスのみをサポートします。詳細については、複数値インデックスおよびインデックスの作成(MySQLモード)を参照してください。
- 適用シナリオ:タグ/分類、多対多関連(例:俳優リストを配列で保存する)、および
MEMBER OF()、JSON_CONTAINS()、JSON_OVERLAPS()などの述語に適しています。
- 適用シナリオ:タグ/分類、多対多関連(例:俳優リストを配列で保存する)、および
空間インデックス:独立した空間インデックス構造で、地理空間データやGISシナリオで使用され、座標範囲内のデータを迅速に検索できます。ローカルインデックスのみをサポートします。
ベクトルインデックス:Vector型列に作成されるベクトルインデックスで、密ベクトルインデックスと疎ベクトルインデックスが含まれます。密ベクトルインデックスの例としてはHNSW、IVFが挙げられます。疎ベクトルインデックスのタイプと制約については、ベクトルインデックスの概要を参照してください。L2、内積、コサインなどの距離/類似度計算をサポートしており、ベクトル類似性検索クエリに適しています。独立したインデックス構造を持ち、ローカルインデックスのみをサポートします。
APシナリオにおける カラムストアインデックス、行ストアベーステーブル + カラムストアインデックス などの組み合わせについては、カラムストアテーブルとカラムストアインデックスの実践を参照してください。全文インデックス、JSON複数値インデックス、空間インデックス、ベクトルインデックス などの特殊なインデックスの選定については、JSON複数値インデックスと全文インデックスの実践を参照してください。その他の構文と実装の詳細については、MySQLモードインデックスの概要およびOracleモードインデックスの概要を参照してください。
データ型
テーブルの作成と使用の前に、データベース管理者はビジネス要件に基づいてテーブル構造とデータ型を適切に計画する必要があります。データストレージの効率とクエリの最適化を確保するため、管理者は以下の原則に従う必要があります:
- テーブル構造の正規化:テーブル構造を適切に設計することで、データの冗長性を最小限に抑え、クエリ効率を向上させます。
- 適切なSQLデータ型の選択:各列に最も適したSQLデータ型を選択することで、ストレージ容量を削減し、クエリ速度を向上させます。
一般的なSQLデータ型には以下のものが含まれます:
- 基本データ型:例えば
INT、VARCHAR、DATEなどです。 - 複合データ型:例えば
JSON、ARRAY、BITMAPなどで、より複雑なデータ構造の格納に適しています。
詳細なSQLデータ型の説明については、以下を参照してください:
ビュー
OceanBaseデータベースは、標準ビューとマテリアライズドビューをサポートしています。
- 標準ビュー(Standard Views):標準ビューは非マテリアライズドビューとも呼ばれ、最も一般的なビューのタイプです。これらはビューの定義を表すSQLクエリのみを格納し、クエリ結果は格納しません。
- マテリアライズドビュー(Materialized Views):マテリアライズドビューは標準ビューとは異なり、物理的にクエリ結果を保持します。OceanBaseデータベースは非同期マテリアライズドビューをサポートしており、基礎テーブルのデータが変更されてもマテリアライズドビューは即座に更新されないため、基礎テーブルのDML操作の実行パフォーマンスが保証されます。マテリアライズドビューは事前集計の一般的な実装方法であり、分析基準が固定されているシナリオやレポート、ダッシュボードなどに適しています。詳細については、MySQLモードのマテリアライズドビューおよびOracleモードのマテリアライズドビューを参照してください。
シナリオの推奨と組み合わせ
ワイドテーブル、事前集計、パーティショニング戦略などのAPテーブル設計原則を組み合わせ、以下の表は典型的なシナリオにおけるテーブル設計の組み合わせ推奨事項を示しており、業務形態に応じた選定に役立ちます。具体的なパーティションキー、パーティション数、ストレージ形式、インデックスは、データ量とクエリパターンに基づいて詳細に決定する必要があります。詳細については、カラムストアと行列混合ストアの概要、APシナリオにおける主キー設計の実践、OLAPシナリオにおけるテーブルパーティショニング設計の実践、カラムストアテーブルとカラムストアインデックスの実践、JSON複数値インデックスと全文インデックスの実践を参照してください。
典型的なシナリオ |
推奨されるテーブル設計の組み合わせ |
説明 |
|---|---|---|
| 純粋な分析、大規模ワイドテーブル、複数列スキャン | カラムストアテーブル + パーティションテーブル(時間/次元別)+ テーブルグループ(複数テーブルJoin時) | ワイドテーブルはカラムストアでI/Oを削減;パーティショニング戦略でプルバックと並列処理を実現;テーブルグループで関連テーブルのパーティションを整列させ、Partition Wise Joinを容易にする。 |
| 固定レポート/ダッシュボード、繰り返し集計クエリ | 詳細テーブル(パーティション+カラムストアまたは行列混合ストア)+ マテリアライズドビュー(事前集計結果) | 事前集計:マテリアライズドビューが集計結果を保持し、クエリは直接読み取り可能;詳細テーブルは引き続きアドホック分析に使用できる。 |
| HTAP、TP主体で分析も併用 | 行ストアベーステーブル + カラムストアインデックス + パーティションテーブル | 書き込みとポイントクエリは行ストアを経由し、分析はカラムストアインデックスを経由することでストレージを制御できる。詳細については、カラムストアテーブルとカラムストアインデックスの実践を参照してください。 |
| TP/APの物理的隔離が必要な場合 | F/R上の行ストアまたは行列混合ストア + カラムストアレプリカ(Cレプリカ) | APトラフィックはCレプリカで弱い読み取りを行い、TPはF/Rを経由する;F/R上に分析用のカラムストアインデックスを構築しなくてもカラムストアスキャンが可能である。デプロイについては、APデプロイの概要を参照してください。 |
| アドホック分析、クエリ列が固定されていない場合 | 行列混合ストアテーブル + パーティションテーブル | オプティマイザーが自動的に行ストア/カラムストアのパスを選択する;パーティションでプルバックとライフサイクル管理を行う。 |
| ログ/イベント追跡、時間範囲によるクエリとクリーンアップ | カラムストアテーブルまたは行列混合ストア + RANGEパーティション(日/時間単位) | パーティショニング戦略:時間によるパーティショニングはプルバックとパーティション単位でのアーカイブ/削除を容易にする;カラムストアは列単位のスキャンと圧縮に適している。 |
上記の組み合わせは重ねて使用できます(例:パーティションテーブル + カラムストア + マテリアライズドビュー)。具体的な選定は、業務の書き込み量、クエリパターン、ストレージコストに基づきます。
データ作成例
パーティション、カラムストア、行ストアのインデックスを含むテーブルを作成します。
CREATE TABLE salesdata (
sale_id INT,
product_id INT NOT NULL,
saledate DATE NOT NULL,
saledate_int INT, -- 通常列として定義
quantity INT,
price DECIMAL(10, 2),
customer_id INT,
PRIMARY KEY (sale_id, saledate_int) -- 主キーには通常列を含めることができます
)
PARTITION BY RANGE COLUMNS (saledate_int) (
PARTITION p2023_q1 VALUES LESS THAN (202304),
PARTITION p2023_q2 VALUES LESS THAN (202307),
PARTITION p2023_q3 VALUES LESS THAN (202310),
PARTITION p2023_q4 VALUES LESS THAN (202401)
)
WITH COLUMN GROUP(each column);
CREATE INDEX idx_product_id ON salesdata(product_id);
CREATE INDEX idx_customer_id ON salesdata(customer_id);
パーティション:
salesdataテーブルは 範囲パーティション(RANGE)を使用します。PARTITION BY RANGE COLUMNS (saledate_int):saledate_int列によってパーティションを分割します。4つのパーティションが定義されています:
p2023_q1パーティションには202304より前のすべてのデータ(つまり2023年第1四半期)が含まれます。p2023_q2、p2023_q3、p2023_q4パーティションは、2023年の第2、第3、第4四半期のデータをそれぞれカバーします。
カラムストア:
WITH COLUMN GROUP(each column);により カラムストア が指定されています。これらの列のデータはカラムストア方式で保存され、大規模なデータ分析シナリオに適しています。行ストアインデックス:
product_id列とcustomer_id列にそれぞれインデックスidx_product_idとidx_customer_idを作成しました。行ストアインデックスは、特定の列に基づくクエリ、特に頻繁に実行される小規模なクエリを高速化することができます。
カラムストアテーブルを作成して使用する際、大量のデータをインポートした場合は、読み取りパフォーマンスを向上させるためにメジャーコンパクションを1回実行し、統計情報収集を行って実行戦略を調整する必要があります。
メジャーコンパクション:データを一括インポートした後、メジャーコンパクションを1回実行することを推奨します。これは、断片化されたデータを整理し、物理的に連続した状態にすることで、読み取り時のディスクI/Oを削減し、読み取りパフォーマンスを向上させるのに役立ちます。データインポート後、テナント内でメジャーコンパクションを1回トリガーし、すべてのデータがベースライン層にマージされていることを確認してください。操作については、MAJOR AND MINOR (MySQLモード)およびMAJOR AND MINOR (Oracleモード)を参照してください。
統計情報収集:メジャーコンパクションが完了した後、統計情報の収集を行うことを推奨します。これは、オプティマイザーが有効なクエリプランと実行戦略を生成するために非常に重要です。GATHER_SCHEMA_STATS (MySQLモード) /GATHER_SCHEMA_STATS (Oracleモード)を実行して、すべてのテーブルから統計情報を収集し、ビューGV$OB_OPT_STAT_GATHER_MONITOR (MySQLモード)およびGV$OB_OPT_STAT_GATHER_MONITOR (Oracleモード)で収集進捗を監視できます。
カラムストアテーブルのデータ量が増加するにつれて、メジャーコンパクションの速度が低下する可能性があるため注意が必要です。