データベースシステムでは一般的に、ストレージコストを削減するためにデータをさまざまな程度で圧縮します。AP向けの一部のカラムストアデータベースでは、列ごとのエンコード圧縮により特定のクエリのパフォーマンスを向上させることができます。しかし、ほとんどの圧縮アルゴリズムでは、圧縮率が高いほど計算が複雑になり、圧縮・解凍の速度も遅くなります。従来のB木ストレージ構造を採用するデータベースでは、データ圧縮はデータ書き込み時にCPUの計算負荷をかけ、書き込み性能に影響を与える可能性があります。しかし、OceanBaseデータベースのLSM-Treeアーキテクチャにより、データ圧縮はCompactionフェーズでのみ行われ、データ書き込みには影響しません。また、これによりより高い圧縮率を持つ圧縮方式を使用でき、一部の顧客のアプリケーションシナリオにおいて、OceanBaseデータベースの圧縮能力の優位性が証明されています。
OceanBaseデータベースでは、MemTableのメモリ使用量が一定のしきい値に達するか、日次マージが発生すると、ダンプ/マージがトリガーされ、MemTable内のデータはディスクにフラッシュされ、静的なSSTableデータにマージされます。MemTableに比べて、SSTableにはより多くのデータ量と相対的に多くのコールドデータが含まれます。マージによって新しいSSTableが継続的に生成される過程で、OceanBaseデータベースはSSTable内のデータを圧縮・エンコードし、ハードディスク上のデータストレージ容量を節約するとともに、SSTableに対するクエリ時のI/O負荷を軽減します。SSTableでは、データはブロック単位で構成されており、2MBの固定長を持つマクロブロックはストレージ空間の管理を容易にします。一方、マクロブロック内の可変長のマイクロブロックはデータ圧縮を容易にします。マイクロブロックにはflatマイクロブロック(未エンコード)とencodingマイクロブロック(エンコード済み)の2種類の格納形式があり、OceanBaseデータベースのデータ圧縮とエンコードはすべてマイクロブロックの粒度で行われます。encoding形式のマイクロブロックは、パディング行 > エンコード > 汎用圧縮(オプション)> 暗号化(オプション)のプロセスを経て構築され、最終的にディスクにフラッシュされるデータブロックとなり、固定長のマクロブロックに書き込まれます。このプロセスにおけるエンコードと汎用圧縮が、OceanBaseデータベースにおけるデータ圧縮の2つの方法です。
一般圧縮
一般圧縮とは、圧縮アルゴリズムがデータの内部構造を理解することなく、データブロックを直接圧縮する手法を指します。この種の圧縮は、バイナリデータの特性に基づいてエンコードし、保存データの冗長性を削減するものであり、圧縮後のデータはランダムアクセスができません。圧縮および解凍はいずれもデータブロック全体単位で行われます。OceanBaseデータベースでは、データブロックに対して zlib、snappy、lz4 および zstd の4種類の圧縮アルゴリズムをサポートしています。zstdとlz4の圧縮レベルは1、zlibは6、snappyはデフォルトの圧縮レベルを使用します。OceanBaseデータベース内部でデフォルトの16kbサイズのマイクロブロックに対する圧縮テストにおいて、snappyとlz4は圧縮速度が速いものの圧縮率は低く、zlibとzstdは圧縮率が高いものの圧縮速度は若干遅いことが確認されました。lz4とsnappyの圧縮率は似ていますが、lz4の方が圧縮・解凍速度が速いです。同様に、zstdの圧縮率はzlibと似ていますが、圧縮・解凍速度はどちらも速いです。MySQLモードでは、ユーザーが上記の圧縮アルゴリズムを個別に選択できますが、OracleモードではOracleの圧縮オプションと互換性があり、ユーザーはlz4またはzstd圧縮アルゴリズムのみを選択できます。
データエンコーディング (Encoding)
一般圧縮の基礎の上で、OceanBaseデータベースは、データベースに対して行と列を混合して保存するエンコーディング方式の圧縮方法 (encoding) を独自開発しました。一般圧縮とは異なり、encodingは圧縮アルゴリズムがデータブロック内部のデータ形式や意味を認識することを前提としています。OceanBaseデータベースはリレーショナルデータベースであり、データはテーブル形式で組織されています。テーブルの各列には固定の型があり、これにより同一列のデータ間には論理的に一定の類似性が保証されます。また、一部のシナリオでは、業務上のテーブルにおいて隣接する行間でもデータがより類似している場合があります。そのため、データを列ごとに圧縮してまとめて保存することで、より高い圧縮効果を得ることができます。そこで、OceanBaseデータベースでは、encoding形式のマイクロブロックを導入しました。すべてのデータを逐一行ごとにブロック内に直列化するフラット形式のマイクロブロックとは異なり、encoding形式のマイクロブロックは行と列を混合して保存します。論理的には依然として一連の行データがマイクロブロック内に存在しますが、マイクロブロックはデータを列ごとにエンコードし、エンコード後の固定長データをマイクロブロック内部のカラムストア領域に保存します。一部の可変長データは依然として可変長領域に行ごとに保存されます。さらに、encodingマイクロブロックではデータへのランダムアクセスが可能であり、マイクロブロック内の1行分のデータを読み取る際には、その1行分のデータのみをデコードすれば済むため、一部の解凍アルゴリズムがデータの一部を読むためにデータブロック全体を解凍する必要がある計算量の増大を回避できます。ベクトル化実行の過程でも指定した列のみをデコードできるため、投影処理のオーバーヘッドを低減できます。
OceanBaseデータベースは、列指向データベースで一般的なディクショナリエンコーディング、ラン・レングス・エンコーディング (Run-Length Encoding)、整数差分エンコーディング (Delta Encoding) など、列ごとに圧縮するための複数のエンコーディング形式を提供しています。格納される列がtimestampやbigintなどの固定長値であり、かつそのマイクロブロック内のデータがすべて特定の値域に分布している場合、整数差分エンコーディングは比較的良好な圧縮効果を発揮します。これは、各行の値とマイクロブロック内の最小値との差分のみを保存し、bit-packingによって実際に保存されるデータ量を削減する方式です。マイクロブロック内のデータのカードナリティ (Cardinality) が低い場合、ディクショナリエンコーディングとRLEエンコーディングは、マイクロブロック内部にディクショナリを構築し、各行の参照を保存することで圧縮を実現します。さらに極端な場合、マイクロブロック内の1列がほぼすべて同一のデータである場合、OceanBaseデータベースは定数エンコーディング (Const) を用いて、定数とマイクロブロック内で定数と等しくない値のみを保存することで、さらに圧縮率を向上させます。
これら一般的なエンコーディングに加えて、OceanBaseデータベースは文字列に対してもいくつかのエンコーディング形式を設計しています。例えば、1列のデータに類似したプレフィックスが存在する場合、プレフィックスエンコーディング (prefix encoding) を用いて、プレフィックスと各行のサフィックスを保存します。1列のデータが固定長の文字列であり、そのうち数バイトが同じである場合、固定長文字列差分エンコーディング (string diff encoding) を用いて、パターン文字列と各行の差分データを保存します。マイクロブロック内の1列の文字列データの文字カードナリティが16未満の場合、16進数を用いてこの文字を表す16進数エンコーディング (Hex encoding) を使用できます。これらの文字列関連のエンコーディングは、長い業務IDや形式付きの文字列データなどに対して良好な圧縮効果を発揮します。
業務上のストレージテーブルでは、同一列データ間に類似性が存在するだけでなく、異なる列データ間にも一定の関係が生じる場合があります。そのため、OceanBaseは列間エンコーディング (span-column Encoding) を導入しました。2列のデータが大部分同じ場合、列等価エンコーディング (Column equal encoding) を使用し、その結果、1列全体がもう1列への参照となります。1列のデータが別の1列のデータのプレフィックスである場合、列プレフィックスエンコーディング (Column prefix encoding) を使用し、完全な1列とその列のサフィックスのみを保存することもできます。このような列間エンコーディングは、データテーブル設計上生じるデータ冗長性を低減でき、繰り返されるタイムスタンプや複合列などに対して良好な圧縮効果を発揮し、マクロブロック全体の圧縮率を向上させることができます。しかし、このような列間エンコーディングは、エンコード時およびデコード時により複雑になります。エンコード時には異なる列データがエンコードルールに適合するかどうかを検出する必要があり、デコード時には参照に基づいて参照された列のデータにアクセスし、処理後にデータをデコードする必要があるため、他のエンコーディングに比べてCPUに負荷がかかります。また、場合によっては異なる列間でカスケード参照が可能な状況が発生することがあり、このような場合には特別な処理を行う必要があります。
各列ごとのエンコードに加えて、OceanBaseデータベースは1列のデータに対して複数のエンコーディング方式を適用して圧縮することもサポートしています。例えば、hexエンコーディングは他の文字列エンコーディングと重ね合わせることができますが、それに応じてエンコードとデコードも複雑になります。null値の保存については、異なるエンコーディング方式や、列指向/行指向ストレージによっていくつかの違いがありますが、ほとんどの場合、nullビットマップを使用して、その列に対応する行のデータがnullかどうかを示します。OceanBaseデータベースでサポートされているエンコーディング形式は、テーブルのスキーマに関連するだけでなく、マイクロブロック内の値域などデータ自体の特性にも関連しています。これは、DBAがテーブルデータモデルを設計する際に列エンコーディングを指定するだけでは最適な圧縮効果を得ることが難しいことを意味します。そのため、OceanBaseデータベースはメジャーコンパクション処理中に適切なエンコーディング方式を自動的に検出し、データをエンコードしてより高い圧縮率を達成することをサポートしています。n列のデータに対してm種類のエンコーディングを検出するには、理論上m×n回のエンコードが必要です。列間エンコーディングを導入するとさらに複雑になるため、エンコード選択アルゴリズムにおいても、OceanBaseデータベースはメジャーコンパクション時のデータエンコード効率を向上させるための最適化を行っています。
V3.2バージョン以降、encodingはベクトル化実行とフィルターダウンプッシュをサポートし、エンコード後のデータに対してエンコードの特徴に基づいてフィルタリングを行うことができるようになりました。これにより、一部のオーバーヘッドが低減され、一部のエンコーディングではフィルタリング効率も向上します。また、一部の列指向ストレージの固定長データでは、AVX2命令セットを使用したSIMDによる高速フィルタリングもサポートしています。同時に、マイクロブロック内部で列指向ストレージとして保存されているデータについては、ベクトル化実行において直接列ごとにデコードすることで、キャッシュや分岐予測にもより適しています。
もちろん、これらの利点に加えて、encodingはいくつかの問題も引き起こす可能性があります。その中で最も直接的なのは、エンコードとデコードに伴う追加のオーバーヘッドであり、メジャーコンパクション処理中のCPU計算負荷、クエリの逐一行反復時の複雑なデコードによるCPUオーバーヘッド、およびデコーダー自体のオーバーヘッドなどが含まれます。OceanBaseデータベースはこれらの問題に対していくつかの最適化を行っており、デコーダーと対応するデータを一緒にメモリにキャッシュするなどの対策を講じています。しかし、一部の複雑なエンコーディング形式では、デコード自体による追加のパフォーマンスオーバーヘッドは避けられません。OceanBaseデータベースは、クエリ性能、メモリ使用量、ストレージコストなどについて、今後もより多くのトレードオフを試みていく予定です。
圧縮オプションの変更
OceanBaseデータベースはDDLを通じてテーブルレベルの圧縮・エンコード方式を設定できます。ただし、SSTableが既に生成されているテーブルの圧縮オプションを変更する場合、一度のメジャーコンパクションに過度のI/O書き込み負荷をかけないために、プログレッシブコンパクションを用いて全てのマイクロブロックデータを段階的に書き直し、圧縮オプションの変更を完了する必要があります。プログレッシブコンパクションのラウンド数は、テーブルレベルの構成パラメータprogressive_merge_numで設定できます。
テーブル作成時の圧縮オプションの指定
MySQLモード:
create table xxx row_format = $value compression = $value;Oracleモード:
create table xxx $value;
テーブルの圧縮オプションの変更
MySQLモード:
alter table xxx [set] row_format = $value compression = $value;Oracleモード:
alter table xxx [move] $value;
MySQLモードにおけるcompressionのオプション値は以下のとおりです。
説明
zlibの異なるマイナーバージョン間で圧縮アルゴリズムが異なる場合、同じデータを圧縮しても結果が一致しないことがあります。OceanBaseデータベースのアップグレード中にレプリカデータの復元ができなくなる問題を防ぐため、V4.3.x系ではV4.3.0以降、zlib_1.0圧縮アルゴリズムはサポートされなくなりました。
none
lz4_1.0
snappy_1.0
zstd_1.0
zstd_1.3.8
lz4_1.9.1
MySQLモードにおけるrow_formatのオプション値は以下の表のとおりです。
値 |
マイクロブロック形式 |
|---|---|
| REDUNDANT | flat |
| COMPACT | flat |
| DYNAMIC | encoding |
| COMPRESSED | encoding |
| CONDENSED | selective encoding、encodingのサブセット。selective encodingはRAW、DICT、CONSTエンコーディングのみをサポートし、データに対してのみバイトパッキングを行います |
| DEFAULT | DYNAMICと同義です |
Oracleモードでの値とその説明は以下の表のとおりです。
値 |
一般圧縮 |
マイクロブロック形式 |
|---|---|---|
| NOCOMPRESS | none | flat |
| COMPRESS BASIC | lz4_1.0 | flat |
| COMPRESS FOR OLTP | zstd_1.3.8 | flat |
| COMPRESS FOR QUERY | lz4_1.0 | encoding |
| COMPRESS FOR ARCHIVE | zstd_1.3.8 | encoding |
| COMPRESS FOR ARCHIVE HIGH | zstd_1.3.8 | encoding |
| COMPRESS FOR QUERY LOW | lz4_1.0 | selective encoding (encodingのサブセット、クエリに優しいエンコーディング方式のみ使用) |