- PostgreSQL 向けの新しいストレージエンジン OrioleDB が、リソース消費の大きい VACUUM プロセスをなくし、テーブル Bloat の問題を解決
- 従来の PostgreSQL は MVCC 構造のため、手動 vacuum と autovacuum が必要で、どちらの方式もかなりのシステムリソースを消費
- OrioleDB は 行・ブロック単位の undo log と 自動ページマージ によって、データ変更を効率的に処理し、断片化したデータを統合
- 合成ベンチマークでは、単一インデックスのみ更新、疎なページの自動マージ、行単位 WAL の適用により、5倍高い TPS とリソース削減を達成
- VACUUM という長年の難題を解決し、メンテナンス負担を減らし、PostgreSQL の 性能・ユーザー体験の改善 を提供
PostgreSQL VACUUM の歴史と限界
- VACUUM プロセスは Berkley Postgres プロジェクトの infinite time-travel 概念に由来する歴史的産物であり、この概念自体は後にコミュニティで廃止された
- しかしその結果、テーブル Bloat に弱い MVCC(Multi-Version Concurrency Control) システム実装につながった
- MVCC は同時トランザクション処理に有利だが、古く不要になったデータを整理する 手動 vacuuming の必要性をもたらした
- 手動 vacuuming は労働集約的な作業であり、システム非効率の潜在的原因でもある
- その後、手動作業の負担を減らすため、自動化された autovacuum が導入された
- 大きな前進ではあったが完全な解決策ではなく、自動であってもかなりのシステムリソースを消費した
- これは Uber が PostgreSQL から MySQL に移行した理由の1つであり、Richard Branson が挙げた PostgreSQL への不満 10 項目の1つとしても言及されている
- Heap-Only Tuples(HOT) 更新 と microvacuum の導入により、テーブル全体の vacuum 必要性は減少した
- それでも VACUUM は依然としてリソース集約的な処理であり、テーブルは Bloat に弱いままだった
- これは OtterTune チームが最も嫌っている PostgreSQL の部分として言及されている
- こうした限界にもかかわらず、堅牢性、拡張性、強力なコミュニティなどを理由に、多くの組織や開発者が PostgreSQL を使い続けている
- OtterTune は問題を認識しつつも PostgreSQL を使い続けることを決めた
OrioleDB の中核機能
- OrioleDB は、テーブルを Bloat から保護し、VACUUM のような定期メンテナンスの必要性をなくすことを主な目標として開発された 新しい PostgreSQL エンジン
- 行単位・ブロック単位の undo log と 自動ページマージ によってこれを実現
- 行・ブロックレベルの undo log はより細かな制御を可能にし、データ変更を効率的に処理する
- 自動ページマージ機能は、バックグラウンドで断片化したデータを継続的に統合する
- 各手法の動作方式
- 行単位 undo log は in-place 更新 を可能にする
- ブロック単位 undo log は、削除されたものの一部トランザクションからは依然見えているタプルを基本ストレージから取り除き、新しいタプルのための空間を確保する
- 疎なページの自動マージにより、多数の削除後でもテーブルとインデックスを Bloat から保護する
- その結果、手動介入が減り、リソース消費が下がり、テーブル Bloat への脆弱性も低下する
ベンチマーク
- テーブル 1 つとインデックス 5 つを作成する初期化スクリプトで合成ベンチマークを構成
id 主キーと value1~value4(float8)、ts(timestamp) カラムを持つ test テーブルを作成
value1, value2, value3, value4, ts にそれぞれインデックスを作成
- pgbench スクリプトは、競合時にインデックス 1 つを疎に更新する upsert 形式
- 1〜10,000,000 の範囲のランダムな
id で INSERT を実行し、競合時は ts のみ更新
- この疎な更新は、通常の heap PostgreSQL テーブルではインデックス Bloat を引き起こす
- このベンチマークが示す OrioleDB 設計の利点
- undo log と in-place 更新のおかげで、OrioleDB は値が変わったインデックス 1 つだけを更新する一方、PostgreSQL の heap エンジンでは単一インデックスフィールドの更新が HOT を無効化し、すべてのインデックスが更新される
- 自動ページマージにより疎なインデックスを Bloat から保護し、疎なページは自動的にマージされる
- 行単位 WAL はブロック単位 WAL よりはるかに少ない容量で済むため、WAL 記録時の IOPS 削減 につながる
- 改善の累積結果として OrioleDB が示した数値
- トランザクションあたり 5 倍高い TPS
- トランザクションあたり 2.3 倍低い CPU 負荷
- トランザクションあたり 22 倍低い IOPS
- テーブルおよびインデックスの Bloat なし
OrioleDB の意義
- OrioleDB の導入により、PostgreSQL コミュニティは VACUUM が過去のものになる新時代に入る
- PostgreSQL の最も古い難題の1つに対する解決策を示し、ユーザーに効率向上とメンテナンス負担の軽減を提供
1件のコメント
Hacker Newsのコメント
それでもいくつか気になる点がある。行単位処理は書き込みの並べ替えや fsync の並べ替えの問題を生み、可変長の行よりもページのほうが並べ替えやすい。
PostgreSQL はデータ安全性に非常に、あるいは極端なくらい保守的で、これは主に適切なタイミングでの fsync によって実現されているが、その影響は SSD ファームウェアまで含む I/O スタック全体に及び、遅くなる。
MVCC は同時アクセスに非常に優れているが、Oriole の文書はグラフがどのような並行性条件で得られたものかを示していない。
Oriole の文書のタイトルと導入部は VACUUM の解決に焦点を当てているが、PostgreSQL で見られる「方形波」グラフが本当に大半は VACUUM のせいだと証明しているようには見えない。Percona の別のベンチマーク(https://www.percona.com/blog/evaluating-checkpointing-in-postgresql/)では、このようなはっきりした方形波パターンは見られない。
著者たちもこうした問題は分かっているはずなので、どう取り組んだのか概要を書いてくれてもよさそうだ。
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
PostgreSQL がデータ安全性に保守的である点を踏まえると、最初の目標は純粋な拡張になることだ。PostgreSQL の一部になるには時間をかけた検証が必要だ。
並行性の情報が欠けていたのはその通りで、ブログ記事に VM の種類と並行性の情報を追加した。
方形波パターンがチェックポイントによって生じたのはその通りだ。ここでの改善要因は実際には VACUUM ではなく、関連するインデックスだけを更新する方式と、全体の I/O を減らす行単位 WAL だ。
すごいとは思うが、親プロジェクトの リリースサイクルにどう追随するつもりなのか気になる。
大規模なオープンソースプロジェクトをフォークしてかなりの変更を載せておきながら、その変更がアップストリームに受け入れられる見込みがないなら、これが良い考えなのか分からない。
単なる遊びで作ったおもちゃなのか、今後も保守していく本気のプロジェクトなのかを明確にすべきだ。
本気のプロジェクトなら、将来捨てられるソフトウェアにならないための計画、元プロジェクトの今後のリリースを取り込む計画、あるいは完全に別プロジェクトとして分岐する計画を明示する必要がある。
拡張なので PostgreSQL アップストリームの変更を取り込めそうではあるが、それが簡単ならそもそも最初から PostgreSQL プロジェクトの一部になっていただろうから、簡単ではなさそうだ。
「その通りです。ただし、まだ道のりは長いです。現在の OrioleDB は PostgreSQL コアパッチとともに提供される拡張です。中期目標は OrioleDB を純粋な拡張にすることで、長期目標は OrioleDB を PostgreSQL コアの一部にすることです」
次は最初からこんなに強い調子で入らないほうがいいと思う。
https://news.ycombinator.com/item?id=36742001
修正例を見ること自体は確かに役に立つ。Postgres フォークを維持するリソースがある会社ならよいし、そうでなければこのフォークを使う難しさが性能向上に見合うものかを評価すればよい。
「UberがPostgresからMySQLへ移行した」といった話は、もうやめてほしい。Uberはリレーショナルデータベースとして使っていたPostgresから、MySQLを分散キー・バリューストアとして使う、実質的に自前の非リレーショナルデータベースへ移したのであって、多くのPostgres利用者にそのまま当てはまる状況ではない
いずれにせよ、古いデータをundoログ/セグメントへ移すこの種のMVCC設計はOracle DBでも使われており、仕組み自体は実証済みである
共通の難しさは、古いバージョンのデータを読む速度が遅くなることだ。ログから探す必要があり、場合によってはトランザクションが終わる前にログからデータが削除され、悪名高いSnapshot Too Oldエラーが起こることもある
記事では、行がいつundoログから削除されるのか見当たらなかった。もう不要になった時点で削除されるのなら、同程度の管理作業が必要になるはずで、どこに改善があるのかよく分からないし、Oracleのように空き領域が尽きうる循環バッファなら、高い書き込み負荷では長時間実行トランザクションが失敗し始めるので、かなり不快だ
「管理作業」が具体的に何を指すかによる。作業量という意味なら、古いundoレコードはファイルをunlinkする形で非常に安価に一括削除できるので、そこに改善がある。VACUUMスキャンは不要だ
空間使用量という意味なら、同じ数のバージョンが同じ量の領域を占めるのはその通りだ。ただ、古い行バージョンを別ストレージに置くことで、基本ストレージの長期的な劣化を防げる。OrioleDBは疎なページの自動マージも実装している
OrioleDBはundoログ用にメモリ内循環バッファを実装している。循環バッファがすべてのundoレコードを収容できない場合、最も古いレコードをストレージへ退避させる。現時点ではundoログサイズに制限を設けておらず、どのトランザクションでも必要となりうる間はレコードを保持するため、「Snapshot Too Old」エラーは発生しない
ただし、undoサイズを制限できるようにするため、Oracle風のエラーをオプションで実装することは検討できる。GitHubのアーキテクチャ文書も確認するとよい
https://github.com/orioledb/orioledb/blob/main/doc/arch.md
もちろんMySQLは、同時実行性への配慮をかなり犠牲にする形でVACUUMを避けている
タイトルには期待したが、第一印象は息苦しかった。GitHubのメインREADME[1]が企業的な宣伝文句のように感じられる
今のところ分かったのは、OrioleDBがPostgreSQL向けの新しいストレージエンジンであること、PostgreSQLが「最も愛される」データベースであること、OrioleDBが他の拡張の上に成り立つ拡張であること、そしてOrioleDBがクラウドへの扉を開くということくらいだ
暗号資産やWeb 3.0の詐欺騒ぎが過ぎ去った今なら、PostgreSQLのように重要なものを拡張・改善するプロジェクトを公開する際に、こういうアプローチは避けたのではないかと思う
[1] https://github.com/orioledb/orioledb
これ以上明確には書きにくいのではないかと思う。テーブルアクセスメソッドは、zheapのような代替ストレージ方式や列指向ストレージをサポートするためにPostgreSQLへ導入された機能だ
この点に触れるのは重要だ。代替データストレージシステムを載せたPostgreSQLフォークはかなり多いが、OrioleDBはフォークしていないPostgreSQL上で拡張として動作するよう設計されているからだ。まだそうなってはいないが、目指している方向はそうである
PostgreSQLに慣れているなら、READMEはかなり明快に見える
Orioleの設計は、トランザクションを認識するインデックスと、点単位でのエントリ削除を要求しているように見え、そこには別のコストがある
例えば、空間インデックス向けのGiSTに相当するものは、各インデックスタプルの位置を正確に特定できない性質のため、維持が厄介になりそうだし、全文検索インデックス向けのGINは、posting treeの圧縮性が落ちて非常に大きくなる可能性がある
また、インデックス構成テーブルを使う状況で、BRINに相当するものをどう実装するのかも想像しにくい。BRINは、興味のあるデータがなければ物理テーブルの大きな範囲をクエリ結果から素早く除外できる。主キー範囲でパーティショニングはできるだろうが、主キーの値密度は時間や値範囲によって大きく変わりうる
こうした、より複雑だが非常に有用なインデックスメソッドをどう実装する計画なのか気になる
照合順序、つまりコレーションが変わったときに起こりうる問題もある。PostgresのヒープとVACUUMはソート順を認識しないため、コレーション変更後に誤った位置にある行を削除して再挿入し、VACUUMが壊れたタプルを最終的に片付けることで損傷を修復できる場合が多い
Orioleでは、削除すべき元のタプルを点検索で見つけられないため、そのやり方は難しそうで、既知のインデックス破損事例を直すにはインデックス全体の再構築が必要になる可能性があり、保守負担がかなり大きく見える
GINに似た機能でも、posting listの圧縮は引き続き可能だ。ありうる選択肢としては、undoレコードをposting listの各項目ではなく、posting list全体に関連付ける方法がある
BRINはインデックス構成テーブルを使うので、直接の類似物を作るのは難しそうだ。ただし、主キーの内部ページにあるunion keyを使って面白いことはできる
コレーションの問題はその通りで深刻だ。GA前には、コレーションを認識するすべてのインデックスを特定のlibicuコレーションバージョンに固定する必要があるだろう
記事の論理も説得力があり、ベンチマークも性能面の主張を裏付けているように見えるが、提案されている新しいストレージエンジン、つまり OrioleDB と PostgreSQL 自体の区別がよく理解できない。
商業的な動機や、記事で扱われているイノベーションで収益を上げたいという理由以外に、この改善をアップストリームへ貢献する代わりに、OrioleDB という新しいデータベースとしてマーケティングすべき理由はあるのだろうか?
ただし、OrioleDB の変更は段階的に入れるには大きすぎる。だから、現在の PostgreSQL エンジン、つまりヒープだけでなく複数の下位サブシステムも含めたものと OrioleDB を比較している
詳しくは https://www.socallinuxexpo.org/sites/default/files/presentations/solving-postgres-wicked-problems.pdf の、特に 9〜11 枚目のスライドを見るとよい
エンジン拡張であれば、他の拡張と併用したときに影響があるのか気になる。たとえば timescaledb[0] は下位テーブルに対して動作する。
このようにした場合にどういう効果があるのか気になる。
create table xyz(...) using orioledb;select create_hypertable(xyz, ts);[0] https://github.com/timescale/timescaledb
OrioleDB が安定した オンディスク形式を約束し、Postgres のメジャーバージョン間でアップグレード手順が不要になることに関心はあるだろうか? この問題を解く機会のように見える
新しい型やそのサポート関数の追加のようなものは、何らかのアップグレード手順を通じて挿入されなければならない。カタログテーブルの列レイアウトを変更する別のカタログ変更もあり、この場合もバージョン間で保存済みデータを更新する手順が必要になる。
アップグレード手順がなければカタログを変更できない。だからこそ PostgreSQL のマイナーバージョンアップグレードだけがバイナリ置き換えだけで可能で、問題なく安全にロールバックできる。
アップグレードが内部 API、プランナ、実行系の変更だけに制限されると、開発は深刻に制約される。OrioleDB がこのアップグレード手順の必要性をなくしてくれる可能性は低そうだ
クラスタアップグレード作業の大半はカタログテーブルの書き換えに由来する。しかも、ほとんどのワークロードでは pg_upgrade は非常に高速なので、どこで大きな利得が生まれると考えているのかもよく分からない
「トランザクションあたりのCPUオーバーヘッドが2.3倍減少」という表現が気になる。PostgresはCPU使用率が5%から65%まで上下するのに、Orioleはずっと90%に張り付いている
予測可能性は良いとしても、低い区間を85%も引き上げるのはかなり心配すべきことではないかと思う
良いニュースは、より強力なCPUへ垂直スケールすればOrioleでさらに高い性能を得られることを意味する点だ。一方でPostgresは、同じやり方では性能が伸び続けないだろう
デスクトップOSの観点ならPostgresサーバーのアイドル時間を別の用途に回せるだろうが、サーバーでは普通は1つの仕事をし、その仕事に最適化されたマシンが求められる
TPSを下げればCPUも比例して下がるだろうし、ここではどこまで高く伸ばせるかを見せようとしているようだ
システムをCPU 60%に制限すれば全体の数値は変わるかもしれないが、同じ使用率でTPSが1.8倍ならいずれにせよ勝ちだ。マーケティング上のごまかしというより、かなり良い数字に見える
高価なサーバーCPUが1ユニットあたりXドルで、60%しか使えず現実的にもそれくらいしか使えないなら、1ユニットあたり0.4Xドルを燃やしているようなものだ
ワークロードを縦に積み上げて1台のマシンを90%まで飽和させられるなら、一般的にはQoSや分離手法を適用して、より低い飽和度と比例性能を維持するのは容易だ。逆は成り立たない。マシン全体の飽和度の60%しか使えずにスケールアウトしなければならないなら、90%以上へ行くには再設計が必要で、ここではまさにその再設計が起きたということだ
改善の積み重ねの結果、OrioleDBはTPSが5倍高く、トランザクションあたりのCPU負荷が2.3倍低く、トランザクションあたりのIOPSが22倍低く、テーブルとインデックスの肥大化がないという特性を提供するとされている
CPU負荷が上下するのはPostgresが「スケーリング」しているからではなく、定期的に性能ボトルネックに陥っているためだ。おそらくVACUUM実行の必要のためで、これはI/Oに非常に敏感だ
そのためPostgresは、クエリ処理にI/Oを使う代わりにクリーンアップ作業にI/Oを使い、TPSとCPU使用率が一緒に急落する
一方Orioleは、はるかに高いスループットをはるかに一貫して扱っている。アクセルを踏み込んだときに安定して100mphで走る車と、ペダルを床まで踏んでいるのに40〜70mphの間を激しく行き来する車なら、どちらを選ぶだろうか?
記事には「Richard BransonがPostgreSQLについて嫌いな10のこと」という、かなり興味をそそるタイトルのリンクがある。調べてみると、そのブログを書いたのはRichardではなくRick Bransonだった
ああ、あの人ではなかった