PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:プロレベル運用 - Day27 拡張機能

SQL PostgreSQL
スポンサーリンク

Day27 後半のゴール

「“動くPostGIS”のイメージと、“拡張と付き合う距離感”を持つ」

前半で、「拡張機能とは何か」「PostGISで位置情報を“空間オブジェクト”として扱える」ところまでは掴めました。
後半では、もう一歩踏み込んで「インデックスとパフォーマンス」「座標系の落とし穴」「他の代表的な拡張」「セキュリティと運用の視点」まで含めて、拡張機能と“現実的に付き合う感覚”を作っていきます。


PostGISとインデックス

「“距離検索”を現実的な速度にするための仕掛け」

前半で、ST_DWithin を使った「半径1km以内の店舗検索」の例を見ました。
あれは少数のデータなら問題ありませんが、店舗が何万件、何十万件と増えてくると、インデックスなしでは現実的な速度では動きません。

PostGISでは、geometry や geography に対して、GiST や SP-GiST といった「空間検索向けインデックス」を張るのが基本です。
イメージとしては、「普通のB-treeインデックスは“1次元の並び”に強いが、空間インデックスは“2次元・3次元の位置”に強い」という違いがあります。

例えば、shops.location にインデックスを張る場合は、こう書きます。

CREATE INDEX idx_shops_location
  ON shops
  USING GIST (location);
SQL

これで、「この地点から近い順」「この範囲の中にあるか」といったクエリを、PostgreSQLが空間インデックスを使って効率よく処理できるようになります。

重要なのは、「PostGISを入れただけでは速くならない」ということです。
geometry 型や空間関数を使い始めたら、「そのカラムに適切なインデックスを張る」という一歩までセットで考える必要があります。
これは、普通のテーブル設計で「検索条件に使うカラムにはインデックスを張る」のと同じ発想ですが、空間データでは特に効いてきます。


座標系(SRID)の話を“怖くないレベル”で押さえる

「“とりあえず4326”ではなく、“何を意味しているか”を知っておく」

前半で、geometry(Point, 4326) という型を使いました。
この「4326」は SRID(空間参照系のID)と呼ばれるもので、「この座標がどんな地図のルールに従っているか」を表します。

ざっくり言うと、こういうイメージです。
4326 は「WGS84」という世界共通の緯度経度のルールで、Google Maps やスマホのGPSなど、多くのサービスが使っている座標系です。
他にも、国や用途ごとに「平面に投影した座標系」がたくさんあり、距離や面積をより正確に計算したいときに使われます。

PostGISでは、「違うSRID同士を混ぜる」とエラーになったり、変な結果になったりします。
例えば、geometry(Point, 4326) と geometry(Point, 3857) をそのまま距離計算しようとすると、「座標系が違うよ」と怒られます。

ここで大事なのは、「自分のアプリがどの座標系を前提にしているかを、最初に決めておく」ことです。
多くのWebサービスでは、「外部APIも含めてWGS84(SRID 4326)で統一する」という選択が現実的です。
そのうえで、「距離計算は geography 型にキャストしてやる」といったパターンを覚えておくと、変なハマり方を避けられます。

座標系の世界は深いですが、Day27の時点では「4326は“普通の緯度経度”」「SRIDが違うと混ぜられない」「最初に1つに決める」という3点が押さえられていれば十分です。


例題:最寄り店舗を距離付きでランキングする

「“距離を計算して並べる”をSQLだけで完結させる」

もう少し実務寄りの例として、「ユーザーの現在地から近い順に店舗を並べる」クエリを考えてみます。
shops テーブルは前半と同じく、location geometry(Point, 4326) を持っているとします。

ユーザーの現在地を、経度・緯度で受け取ったとき、こういうクエリが書けます。

SELECT
  id,
  name,
  ST_Distance(
    location::geography,
    ST_SetSRID(ST_MakePoint(139.7671, 35.6812), 4326)::geography
  ) AS distance_m
FROM shops
ORDER BY distance_m
LIMIT 10;
SQL

ここでやっていることを分解すると、こうなります。

location と現在地の点を、どちらも geography 型にキャストしている。
ST_Distance で「2点間の距離(メートル)」を計算している。
その距離を distance_m というカラム名でSELECTし、ORDER BYで昇順に並べている。
LIMIT 10 で「近い順に10件だけ」を取っている。

これにインデックス(GiST)を組み合わせると、「最寄り店舗10件」をかなり高速に取れるようになります。

この例のポイントは、「アプリ側で距離計算をしなくていい」ということです。
アプリは「現在地の経度・緯度を渡して、結果を受け取る」だけで済みます。
距離計算のロジックや最適化は、PostGISとPostgreSQLに任せられます。

こういう「重い計算をDB側に寄せる」判断は、性能面でもコードのシンプルさの面でも、かなり効いてきます。


PostGIS以外の“よく使う拡張”をざっくり知っておく

「“あ、これ拡張でできるかも”と思える引き出しを増やす」

PostGISは地理情報の代表ですが、PostgreSQLには他にも「入れておくと世界が変わる」拡張がいくつかあります。
ここでは名前と用途だけ、軽く頭に入れておきましょう。

uuid-ossp
UUIDを生成する関数を提供する拡張です。
uuid_generate_v4() などを使って、主キーやトークンにUUIDを簡単に使えるようになります。

pg_trgm
トライグラム(3文字の部分列)を使った曖昧検索用の拡張です。
LIKE検索や部分一致検索を高速化したり、「似ている文字列」を探したりできます。
検索機能のあるサービスではかなり役立ちます。

pgcrypto
暗号化・ハッシュ化の関数を提供する拡張です。
パスワードのハッシュ、トークン生成、データの暗号化などに使えます。
セキュリティ的に重要な処理をDB側に寄せたいときの選択肢になります。

これらを全部いきなり使いこなす必要はありません。
大事なのは、「PostgreSQLには、こういう拡張がある」「困ったときに“拡張で解決できないか”を思い出せる」という引き出しを持つことです。


拡張機能とセキュリティ・運用

「“便利なネイティブコード”を入れるということの重さ」

拡張機能は、PostgreSQLのプロセスの中で動くネイティブコード(C言語など)です。
これはつまり、「DBサーバの権限で動くコードを追加する」ということでもあります。

セキュリティの観点からは、次のようなことを意識しておきたいです。

どの拡張がインストールされているかを把握しておくこと。
不要な拡張は入れないこと。
信頼できるソース(公式や実績のあるプロジェクト)の拡張だけを使うこと。

運用の観点では、

PostgreSQL本体のバージョンアップ時に、「使っている拡張が対応しているか」を確認すること。
バックアップ・リストア時に、「拡張がインストールされた環境でリストアする」こと。
本番とステージングで「拡張の有無・バージョン」を揃えること。

などが重要になります。

特にPostGISのような大きな拡張は、「PostgreSQLのバージョンアップ=PostGISのバージョンアップもセット」という世界になります。
これは「面倒だからやめよう」ではなく、「それでも得られるメリットが大きいなら採用する」という判断になります。


「拡張を使う/使わない」を決める視点

「“今の自分たちにとっての得と縛り”を天秤にかける」

最後に、「この機能、拡張でやるべきか?」と迷ったときの考え方をまとめます。

一つ目は、「その機能がどれだけ価値を生むか」です。
PostGISのように、「自前で実装したら地獄を見るレベルの専門性」を一発で手に入れられるなら、拡張を使う価値は高いです。
逆に、「ちょっとした便利関数」程度なら、アプリ側で実装したほうが依存が少なくて済むこともあります。

二つ目は、「その拡張にどれだけ縛られてもいいか」です。
将来、別のRDBMSに移行する可能性が高いなら、「PostGIS前提の設計」は移行コストを上げます。
逆に、「PostgreSQLを長く使い続ける前提」であれば、拡張を積極的に使う選択もありです。

三つ目は、「チームとして運用できるか」です。
拡張のインストール・バージョン管理・本番とステージングの整合性などを、
ちゃんと運用フローに組み込めるかどうかを考えます。

完璧な答えはありません。
でも、「拡張を入れる=便利さと引き換えに、その拡張に責任を持つ」という感覚を持って選べるようになると、
あなたのDB設計のレベルは一段上がります。


Day27 後半のまとめ

PostGISのような拡張は、geometry/geography 型と空間インデックス(GiSTなど)を組み合わせることで、「ある地点から半径1km以内の店舗を探す」「現在地からの距離を計算して近い順に並べる」といった処理を、アプリ側ではなくSQLだけで現実的な速度で実現できる一方で、SRID(4326など)のような座標系の概念を理解し、最初に「どの座標系で統一するか」を決めておかないと、後から混乱やバグを生みやすい。
さらに、PostGIS以外にも uuid-ossp(UUID生成)、pg_trgm(曖昧検索)、pgcrypto(暗号・ハッシュ)など強力な拡張があり、「困ったときに“拡張で解決できないか”を思い出せる引き出し」を持つことは大きな武器になるが、拡張はDBプロセス内で動くネイティブコードでもあるため、「どの拡張を入れているかを把握し、不要なものは入れず、バージョンアップやバックアップも“拡張前提”で設計する」という責任もセットで引き受ける――この“便利さと縛りを天秤にかけて拡張を選ぶ”感覚まで持てれば、Day27 後半としてはかなり良いところまで来ている。

タイトルとURLをコピーしました