The EDB Blog
September 26, 2017

Since I just committed the last pending patch to improve hash indexes to PostgreSQL 11, and since most of the improvements to hash indexes were committed to PostgreSQL 10 which is expected to be released next week, it seems like a good time for a brief review of all the work that has been done over the last 18 months or so.

 Prior to version 10, hash indexes didn't perform well under concurrency, lacked write-ahead logging and thus were not safe in the face either of crashes or of replication, and were in various other ways second-class citizens.  In PostgreSQL 10, this is largely fixed.

Enterprise-ready Postgres tools for high availability, monitoring, and disaster recovery. Download Now.

私がいくつかの設計に携わっていた時は、ハッシュindexの改善のためのクレジットは、まず私の同僚であるAmit Kapila氏に向かいました。彼のこのトピックのブログエントリには、一読の価値があります。 ハッシュindexの問題は、先読みログのコードを書くのが面倒だっただけではなく、実際に正しく動作するような先読みログを追加できるようにコードが構成されていないことでした。 バケットを分割するには、システムは既存のバケットをロックし(かなり非効率なロック機構を使用)、タプルの半分を新しいバケットに移動し、既存のバケットを圧縮してロックをリリースします。 個々の変更が記録されたとしても、間違ったタイミングでクラッシュすると、indexが破損した状態になる可能性があります。 そこで、Amit氏の最初のステップは、ロック機構を再設計することでした。 新しいメカニズムにより、スキャンとスプリットがある程度並行して進行し、エラーやクラッシュによって中断された分割を後で完了することができます。 バグが修正され、いくつかのリファクタリング作業が完了すると、Amit氏のもう1つのpatchにハッシュindexesの先読みログサポートが追加されました。

ところで、ここ数年でbtreeに加えられた多くの比較的わかりやすいパフォーマンスの改善項目において、ハッシュインデックスが落ちている事がわかりました。  ハッシュインデックスは先行書き込みログではサポートされておらず、古いロック機構が非常に重いものであったため、さらにパフォーマンスを改善させたいと言うモチベ―ションがあまりなかったのです。しかし、これはハッシュインデックスが真に有効なテクノロジーであったのであれば、単に先行書き込みログを追加する以外に他に実行すべきことがあった事を意味します。 PostgresSQLのインデックスアクセス方式の抽象化レイヤーは、インデックスによって、インデックスに関する情報のバックエンドプライベートキャッシュを保持する事が可能であり、インデックス自体は所定のメタデータを取得するために繰り返して参照される必要はありません。  btree と spgist インデックスはこのメカニズムを使用していましたが、ハッシュインデックスはそうではないため、私の同僚のミスュン・サイは このメカニズムを使って、ハッシュインデックスのメタページをキャッシュするパッチを書きました。  同様に、btreeインデックスはインデックスページから不用なインデックスポインターを日和見的に削除し、放置すると発生する可能性のある膨大な量のインデックスの膨張を回避する、「シングルページ真空化」と呼ぶ最適化を持っています。  私の同僚であるアシュトシュ・シャーマは、このロジックを ハッシュインデックス上にポート化するパッチを書き、そこでの同様のインデックスの膨張を画期的に減らしました。  最後に、 2006年以来 btreeのインデックスは、同一のインデックスページが繰り返しロックおよびアンロックされる事を防止する機能を持ち、代わりにページから一回でタプルをスラープし、一度に一つづつ返すようにしました。  アシュトシュ・シャーマはさらに、このロジックを ハッシュインデックスにポートしましたが、時間が足らずに最適化はv10には採用されませんでした。  このブログ内容に述べられている全体の内、v11までに実現しない唯一の改善はこれのみです。

ハッシュインデックス関連でより興味深い側面の1つに、動作が実際に正しいのかを決定する際の困難さがあります。  ロック動作に関する変更は、並列処理が多用される場合にのみ失敗しますが、クラッシュ回復の場合には先行書き込みログのバグが、恐らくは唯一の兆候です。  さらに各ケースごとに、問題は捉えにくいものです。  物事がクラッシュなしで走っていると言うだけでは十分ではありません。あらゆる場面で正しい答えが作成されなくてはならず、これが検証する事が難しいのです。  そのタスクを支援するために、私の同僚クンタル・ゴーシュが、ヘイッキ・リナカンガスとマイケル・パキアが当初始めた仕事をフォローアップし、開発者のテスト用のプライベートパッチとしてのみでなく実際に PostgresSQLに注力した、WAL一貫性チェッカーを作成しました。  ハッシュインデックスの先行書き込みログコードは、コミットする前にこのツールを使って徹底的にテストされ、バグの発見に非常に効果がありました。  しかし、このツールはハッシュインデックスのみに限りません:ヒープ、今日我々が持つ全てのインデックスAM、そして将来開発されるその他のものを含めて、他モジュール用の先行書き込みログコードの妥当性検証にも使用することができます。 事実、これは BRINのバグを発見する事に成功しています。

wal_consistency_checkingは、主に開発者用ツールですが、バグが疑われる場合にもユーザーが使用するのに適しています。そこでは、DBA向けのいくつかのツールもアップグレードがなされました。Jesper Pedersen氏は、Ashins Sharma氏がさらに実行し、Peter Eisentraut氏がテストケースを寄稿した、ハッシュindexesのサポートでpageinspect contribモジュールをアップグレードするpatchを書きました(それは本当に良いアイデアでした。これらのテストケースは即座に失敗したため、数回のバグ修正を引き起こしました)。pgstattuple contribモジュールも、Ashutosh Sharma氏の作業により、ハッシュindexesのためのサポートを得ています。

その途中で、パフォーマンスの改善もいくつかありました。私が最初に気づいていなかったことの1つは、ハッシュindexesがバケット分割の新しいラウンドを開始すると、ディスク上のサイズが急激に倍増する傾向があることでした。これは、実際には1MBのindexでは問題にはなりませんが、64GBのindexがあるとその限りではありません。Mithun Cy氏は、倍増を4段階に分けるためのpatchを書いてこの問題をある程度解決しました。つまり、64GBから128GBに一気に変わるのではなく、64GBから80GB、96GB、112GB、そして128GBへと小刻みに変わるようにします。これは、さらに改善することができますが、ディスク上のフォーマットをより深く再構築し、検索パフォーマンスへの影響を注意深く考慮する必要があります。

7月の「AP」に参加したテスターからの報告書は、いくつかの調整が必要であることを私たちに教えてくれました。APは、新しく作成されたハッシュindexに20億行を挿入しようとすると、エラーが発生することを発見しました。この問題に対処するために、Amit氏は各分割の直後に古いバケットのクリーンアップを試みるようにバケット分割コードを修正し、オーバーフローページの蓄積を大幅に削減しました。念のために、Amit氏と私は、オーバーフローページの割り当てを追跡するために使用されるビットマップページの最大数を4倍に増やしました。

それ以外にもいろいろなことをなすこともできましたが、同僚と私は、PostgreSQLコミュニティの他のメンバーの助けを借りて、ハッシュindexesを、放置され、間違いのある未熟な機能ではなく、一級品の機能にするという目標を達成しました。しかし、その機能のユースケースがどのようなものであるかはよく分かります。この記事の冒頭で私が参照(リンク)したAmit氏のブログエントリーは、pgbench作業負荷であっても、ハッシュindexは、低レベルと高レベルの並行処理の両方でbtreeを上回る可能性があることを示しています。しかし、それは、実際には最悪のケースです。ハッシュindexesのセールスポイントの1つは、indexに実際の索引をつけた値ではなくハッシュ値が格納されていることです。そのため、UUIDや長い文字列などの幅の広いキーでは、改善が大きくなると予想しています。それについては、私たちが読み込みと同じ程度の書き込みを最適化していないため、読み込み負荷の高い作業負荷でより力を発揮する傾向があります。しかし、私はこの技術に興味を持っている人には、それを試してメーリングリストに結果を投稿する(または個人的にメールを送る)ようお勧めします。なぜなら、このような機能の本当の鍵は、開発者が実験室で起こる性質のものではなく、現場で実際に起こることだからです。

最後に、Jeff Janes氏とJesper Pedersen氏に、このプロジェクトと一般の両方に関連した貴重なテスト作業を行っていただいたことに心から感謝したいと思います。この規模のプロジェクトを正確に取得することは簡単ではなく、破壊的な要素をなくすと決心した粘り強いテスターを持つことは、大きな助けになります。また、ここに、まだ紹介していなかったテスト、レビュー、そして他のさまざまなサポートをしていただいた方たちを列記されていただきます:Andreas Seltenreich氏、Dilip Kumar氏、 Tushar Ahuja氏、Álvaro Herrera氏、Michael Paquier氏、Mark Kirkwood氏、Tom Lane氏、Kyotaro Horiguchi氏。皆さん、本当にありがとうございました。また、もし表記し忘れてしまった方がいましたら、彼らにもまた感謝の気持ちを捧げたいと思います。

Robert Haasは、EnterpriseDBの副社長であり、チーフデータベースアーキテクトです。 

この投稿記事は、もともとRobert氏の個人的なブログに掲載されていたものです。

robert.haas_enterprisedb.comの写真

Robert is Chief Architect, Database Server, employed at EnterpriseDB as well as a PostgreSQL Committer. Robert is an expert in OLTP query tuning, schema design, triggers and stored procedures, and internals development, as well as an experienced UNIX/Linux system administrator. Additionally,...