PostgreSQLにすてきな機能が追加されたので、それを活用するツールを作ったよ。
データベースの選択肢はいろいろあって、どれが最適かは状況によって変わる。 このセクションでは PostgreSQL をとりあげる。多くの機能を備えた *1 オープンソースのRDBだ。 でも、ここでの説明から一般的なアプローチを読み取って、他のデータベースに適用することもできるだろう。
つい最近まで、PostgreSQLで変更データのストリームを取得するにはトリガーを使うしかなかった。 できないことではないけど面倒だし、スキーマを変更しないといけないし、パフォーマンスもよくなかった。 でも、2014年12月リリースのPostgreSQL 9.4で導入された新機能が状況を一変させた。それが「ロジカルデコーディング」だ。
ロジカルデコーディングによって、PostgreSQLにおける変更データのキャプチャは一気に現実的になった。 そこで私は、この機能がリリースされたときに、この機能を生かしたPostgreSQL用の変更データキャプチャツールを作ることにした。 Confluentの支援を得て(ありがとう!)開発を進め、α版をオープンソースとしてリリースした。それが Bottled Water だ(図3-3)。
現時点のBottled Waterはスタンドアロンのツールで、一貫性のあるスナップショットと変更ストリームをPostgreSQLからKafkaにコピーする。 将来的にはこれをKafka Connectフレームワークに組み込んで、よりデプロイしやすくするつもり。
図3-3:Bottled Water is what you get if you take a stream and package it up in a form that's easy to transport and consume.
「ロジカルデコーディング」という名前の由来は、データベースのWAL(Write Ahead Log:ログ先行書き込み)をデコードするという事実による。 WALについては第2章でも扱った。確か、Bツリーのクラッシュ耐性を高めるとかいう文脈だったはず(図2-16)。 PostgreSQLは、クラッシュリカバリー以外にレプリケーションにもWALを使っている。 後続ノードは、WALの変更をデータベースのコピーに適用し続ける。まるで常にクラッシュリカバリーをやっているようなもの。
レプリケーションの実装としてはうまい方法だけれど、弱点もある。 WALに記録されたログはきわめて低レベルなもので、PostgreSQLの内部データ構造へのバイト単位の変更などを記録している。 これは、WALを自前でデコードしようとするアプリケーションにとっては扱いづらい。
ここで登場するのがロジカルデコーディング。これはWALをパースして、行単位の変更イベントを扱えるようにする機能だ。 テーブルに対する行単位のinsert、update、deleteが、それぞれ個別のイベントになる。 これらのイベントはトランザクション単位でグループ化されていて、データベースにコミットされた順に発生する。 異常終了したりロールバックされたりしたトランザクションの情報は、ストリームには現れない。 なので、ストリームのイベントを同じ順で適用しつつければ、トランザクション的に一貫性のあるデータベースのコピーを得られる。 まさにこれが、変更をキャプチャしたい私たちの求めていたものだ。
PostgreSQLのロジカルデコーディングはとてもよくできていて、変更ストリームとうまく連携できるような一貫性のあるスナップショットも作ってくれる。 このスナップショットを使えば特定の時点におけるデータベース全体のコピーを作れて(このときにデータベースをロックする必要はなく、コピー中にデータベースへの書き込みを続けてもかまわない)、それ以降に発生したイベントを変更ストリームから適用することができる。
Bottled Waterはこの機能を活用してデータベースの中身を取り出す。そしてAvro を使ってバイナリフォーマットに変換する。 変換されたデータはKafkaに送られて、これをさまざまな方法で活用できるようになる。 Elasticsearch用のインデックスを作ったりキャッシュを構築したり、Kafka Streamsなどのフレームワークに食わせたり Kafka HDFS connector を使ってHDFSに読み込んだりなどなど。 いったんKafkaに取り込んでしまえば、数々のサブスクライバがそれを使うときにPostgreSQL側には一切の負荷がかからなくなる。