データベースシステムでは一般的に、ストレージコストを削減するためにデータをさまざまな程度で圧縮します。また、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マイクロブロックではデータへのランダムアクセスが可能であり、マイクロブロック内の特定の行データを読み取る際には、その行データのみをデコードすることで、一部の解凍アルゴリズムがデータブロック全体を解凍する必要がある場合の計算量増大を回避できます。また、ベクトル化実行の過程でも指定された列のみをデコードすることができ、プロジェクションのオーバーヘッドを低減できます。
OceanBaseデータベースは、カラムストアデータベースで一般的な辞書エンコーディング、ラン・レングス・エンコーディング(Run-Length Encoding)、デルタ・エンコーディング(Delta Encoding)など、複数の列圧縮用エンコーディング形式を提供しています。ストレージされる列がtimestampやbigintなどの固定長値であり、かつそのマイクロブロック内のデータがすべて同一の値域に分布している場合、デルタ・エンコーディングは良好な圧縮効果をもたらします。これは、各行の値とマイクロブロック内の最小値との差分のみを格納し、bitパッキングを行うことで実際にストレージされるデータ量を削減する仕組みです。マイクロブロック内のデータのカード性(Cardinality)が比較的小さい場合、辞書エンコーディングとRLEエンコーディングは、マイクロブロック内部に辞書を構築し、各行のインポインタを格納することで圧縮を実現します。さらに極端なケースでは、マイクロブロック内の一列がほぼ同一のデータである場合、OceanBaseデータベースは定数エンコーディング(Const)を用いて、定数とマイクロブロック内で定数に等しくない値のみを格納することで、圧縮率をさらに向上させます。
これらの一般的なエンコーディングに加えて、OceanBaseデータベースは文字列に対してもいくつかのエンコーディング形式を設計しています。例えば、一列のデータに類似したプレフィックスが存在する場合、プレフィックスエンコーディング(prefix encoding)を使用し、プレフィックスと各行のサフィックスを格納します。一列のデータが固定長の文字列であり、そのうち数バイトが同一である場合、固定長文字列差分エンコーディング(string diff encoding)を使用し、モード文字列と各行の差分データを格納します。マイクロブロック内の一列の文字データの文字カード性が16未満の場合、16進数を用いてその文字を表す16進エンコーディング(Hex encoding)を使用できます。これらの文字列関連エンコーディングは、長い業務IDやフォーマット付きの文字列データなどに対して良好な圧縮効果をもたらします。
業務ストレージテーブルでは、同一列のデータ間に類似性が存在するだけでなく、異なる列間にも一定の関係性が存在する場合があります。そのため、OceanBaseは列間エンコーディング(span-column Encoding)を導入しました。二つの列のデータが大部分同一である場合、列間等値エンコーディング(Column equal encoding)を使用し、一列全体が別の列のインポインタとなります。また、一列のデータが別の列のデータのプレフィックスである場合、列間サブストリングエンコーディング(Column prefix encoding)を使用し、完全な一列とその列のサフィックスのみを格納します。このような列間エンコーディングは、データテーブル設計において生じるデータの冗長性を低減し、重複するタイムスタンプや複合列などに対して良好な圧縮効果をもたらし、マクロブロック全体の圧縮率を全体的に向上させることができます。しかし、この列間エンコーディングはエンコードおよびデコード時により複雑になり、エンコード時には異なる列データがエンコーディングルールに適合するかどうかを検出する必要があり、デコード時には参照される列のデータに基づいて処理を行った後にデータをデコードするため、他のエンコーディング方式と比較してCPUにとってやや負荷がかかります。同時に、場合によっては異なる列間でカスケード参照が可能な場合があり、このような状況には特別な処理が必要です。
各列ごとのエンコーディングに加えて、OceanBaseデータベースは一列のデータに対して複数のエンコーディング方式を組み合わせて圧縮することもサポートしています。例えば、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のサブセットで、クエリにより適したエンコーディング方式のみを使用) |