データベースシステムでは、ストレージコストを削減するために一般的にデータを様々な程度で圧縮します。一部の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のサブセット、クエリにより適したエンコーディング方式のみ使用) |