やあ。また会ったね。今日は少しニッチだけど、知っておくと「あ、こいつできるな」と思われるような、PostgreSQLの奥の手について話そうか。
テーマは「Multicorn」だ。
PostgreSQLは強力なRDBMSだけど、実務では「PostgreSQLの中にあるデータ」だけで完結することなんてまずないよね。S3にあるログファイル、Redisのキャッシュ、あるいは社内の怪しげなREST API……。これらをいちいちPythonでスクレイピングしてDBに流し込んで……なんてやってたら、夜が明けてしまう。
そこで登場するのが「外部データラッパー(FDW)」だ。今回は、PythonでサクッとFDWを書ける神ライブラリ「Multicorn」を使って、PostgreSQLを「何でも屋」に変える方法を伝授するよ。
—
なぜ「Multicorn」なのか?
普通、PostgreSQLで自作のFDWを作ろうと思ったら、C言語でゴリゴリ書く必要がある。これは正直、生産性が悪いし、メモリリークのリスクを考えると気が重いよね。
Multicornは、その名の通り「多角形」のパワーで、PythonだけでFDWを書けるようにしてくれる。要は「Pythonのクラスを書くだけで、PostgreSQLのテーブルとして振る舞う」ことができるんだ。
実際に手を動かしてみよう:CSVをテーブルとして扱う
まずは簡単な例からいこう。ファイルシステム上のCSVを、まるでローカルのテーブルかのように`SELECT`するFDWだ。
1. インストール
環境によるけど、基本的にはPostgreSQLの拡張としてインストールする。
Ubuntu系の例
sudo apt-get install postgresql-14-python3-multicorn
2. Pythonでラッパーを書く
Multicornのクラスを継承して、`execute`メソッドを実装するだけだ。
from multicorn import ForeignDataWrapper
import csv
class SimpleCSVFDW(ForeignDataWrapper):
def __init__(self, options, columns):
super(SimpleCSVFDW, self).__init__(options, columns)
self.path = options[‘path’]
self.columns = columns
def execute(self, quals, columns):
with open(self.path, ‘r’) as f:
reader = csv.DictReader(f)
for row in reader:
yield row
これをサーバーの適切なパス(`PYTHONPATH`が通っている場所)に置く。
3. PostgreSQL側で宣言する
ここが面白いところだよ。SQLでPythonのコードを呼び出すんだ。
CREATE EXTENSION multicorn;
CREATE SERVER my_csv_server FOREIGN DATA WRAPPER multicorn
OPTIONS (wrapper ‘my_module.SimpleCSVFDW’);
CREATE FOREIGN TABLE my_csv_table (
id integer,
name text
) SERVER my_csv_server
OPTIONS (path ‘/tmp/data.csv’);
これだけで、`SELECT FROM my_csv_table;` と打てば、PythonがCSVをパースして結果を返してくれる。SQLのJOINだって、WHERE句によるフィルタリングだって、PostgreSQLのエンジンが勝手に最適化して処理してくれる。最高だろ?
—
実務で意識すべき「ハマりどころ」
さて、ここからは現場のエンジニアとしての忠告だ。Multicornは便利だけど、魔法の杖じゃない。以下の点には注意してくれ。
1. パフォーマンスは「Python次第」
当たり前だけど、Pythonの処理速度がボトルネックになる。数百万件のデータをループで回すような実装をすると、PostgreSQLのプロセスごと重くなる。
- 解決策: 可能なら`quals`(WHERE句の条件)をPython側で受け取り、APIのリクエストやファイル検索時に「絞り込み」を先に行うこと。これを「プッシュダウン」と呼ぶ。これをやらないと、全件取得してからPostgreSQL側でフィルタリングすることになり、ネットワークとメモリが死ぬ。
2. ネットワーク越しのデータソースに注意
REST APIを叩くFDWを作るなら、タイムアウト対策は必須だ。DBのコネクションがAPIのレスポンス待ちで埋め尽くされると、サイト全体がダウンする。コネクションプールや非同期処理、あるいは適度なキャッシング層を間に挟む設計を心がけてほしい。
3. トランザクションの整合性
FDWは「外部」を見に行くわけだから、PostgreSQLのACID特性を完全に保証するのは難しい。読み取り専用として使う分にはいいけど、書き込み(INSERT/UPDATE)を実装する場合は、エラーハンドリングを死ぬほど丁寧にやってくれ。
—
最後に:データベースエンジニアの視点
僕は、「データベースはデータの『ハブ』であるべきだ」と思っている。
アプリケーション側で何でもかんでもデータを結合するのではなく、SQLという強力なインターフェースで、バラバラにあるデータソースをひとまとめにする。Multicornを使えば、その設計が驚くほどスマートになる。
「APIを叩くためのバッチ処理」を書いて管理するくらいなら、Multicornでテーブルとしてマッピングして、`VIEW`で綺麗に整える。これだけで、運用コストは劇的に下がるはずだ。
もし「こんなデータソースを繋ぎたいんだけど、どう設計すればいい?」という悩みがあれば、いつでも相談してくれ。技術の引き出しは、開けっ放しにしておくのが一番だ。
それじゃあ、またコードの世界で会おう。健闘を祈るよ。
コメント