PostgreSQL入門:PostgreSQLとは何か、そしてなぜそれを選ぶのか
PostgreSQLは世界で最も先進的なオープンソースのリレーショナルデータベースです。信頼性、拡張性、標準準拠で知られ、Apple、Instagram、Spotifyなどのグローバルリーダーに信頼されています。
1. 学習内容
- データベースとは何か、リレーショナルと非リレーショナルの違い
- PostgreSQLの歴史と設計思想
- PostgreSQL vs MySQL:主な違い
- PostgreSQLの主要な強み(ACID / MVCC / 拡張性 / JSONB / 全文検索)
- PostgreSQLの一般的なユースケース
2. フルスタック開発者の実体験
(1) 悩み:データベース選定は混乱する
Aliceはフルスタック開発者で、彼女の会社は新しいEコマースプロジェクトを立ち上げようとしています。技術選定の会議で、チームはMySQLとPostgreSQLの間で議論を交わしました:
- MySQLを主張する人:「ずっとMySQLを使ってきたから、切り替える必要はない。」
- PostgreSQLを推す人:「PGはJSONB、全文検索、ウィンドウ関数をサポートしていて、MySQLよりはるかに高機能だ。」
- Aliceは混乱しました:両者の本質的な違いは何なのか?間違った選択をした場合、後々の移行コストは高くなるのか?
彼女は客観的で包括的な比較を必要とし、それに基づいて決定を下さなければなりませんでした。
(2) PostgreSQLのソリューション
PostgreSQLは、従来のリレーショナル検索からJSONドキュメントストレージ、全文検索からベクトル検索まで、単一のデータベースでほとんどのビジネスニーズをカバーします。余分なミドルウェアは不要です:
-- PostgreSQL:複数のユースケースを1つのデータベースで
-- 1. 従来のリレーショナル検索
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
-- 2. JSONBの柔軟なストレージ(MongoDB不要)
INSERT INTO products (name, attributes)
VALUES ('ランニングシューズ', '{"color": "red", "size": 42, "tags": ["sport", "outdoor"]}'::jsonb);
-- 3. 全文検索(Elasticsearch不要)
SELECT name FROM products
WHERE to_tsvector('english', name) @@ to_tsquery('english', 'running & shoes');
(3) 得られた成果
AliceはPostgreSQLを選ぶことが以下の意味を持つと気づきました:
- 2〜3個のミドルウェアコンポーネントを削減(MongoDB / Elasticsearch / Redisキャッシュ層が不要)
- 開発効率の向上:JSONB + 全文検索 + ウィンドウ関数で、複雑なニーズを1つのSQL文で解決
- 長期的なコスト削減:PostgreSQLは完全にオープンソースで無料、ライセンス料は不要
3. データベースとは何か
データベースとは、データを構造化された方法で整理、保存、管理するシステムです。データベースがなければ、アプリケーションのデータはメモリ上にしか存在せず、プログラムを閉じた瞬間に消えてしまいます。
(1) リレーショナル vs 非リレーショナルデータベース
| 観点 | リレーショナル(RDBMS) | 非リレーショナル(NoSQL) |
|---|---|---|
| データモデル | テーブル(行+列)、厳格なスキーマ | ドキュメント / キーバリュー / グラフ / ワイドカラム、柔軟なスキーマ |
| クエリ言語 | SQL(標準化) | 独自のAPI |
| トランザクション | ACIDによる強整合性 | ほとんどが結果整合性のみ |
| 事例 | PostgreSQL、MySQL、Oracle | MongoDB、Redis、Cassandra |
| ユースケース | 複雑な検索、トランザクション処理、高整合性ニーズ | 高スループット、柔軟な構造、迅速な反復 |
| スケーリング | 主に垂直 | 主に水平 |
graph TB
DB[データベース] --> RDBMS[リレーショナルDB<br/>SQL + ACID]
DB --> NoSQL[NoSQL<br/>柔軟なスキーマ]
RDBMS --> PG[PostgreSQL]
RDBMS --> MY[MySQL]
RDBMS --> OR[Oracle]
NoSQL --> MGO[MongoDB<br/>ドキュメント]
NoSQL --> RED[Redis<br/>キーバリュー]
NoSQL --> CAS[Cassandra<br/>ワイドカラム]
4. PostgreSQLの歴史
(1) POSTGRESからPostgreSQLへ
graph LR
A["1986年<br/>POSTGRESプロジェクト<br/>UC Berkeley"] --> B["1995年<br/>Postgres95<br/>SQLサポート追加"]
B --> C["1996年<br/>PostgreSQL 6.0<br/>オープンソースリリース"]
C --> D["2010年代<br/>JSONB / CTE / ウィンドウ<br/>関数 / FDW"]
D --> E["2024年<br/>PostgreSQL 17<br/>現行LTSリリース"]
| 年 | マイルストーン | 意義 |
|---|---|---|
| 1986 | POSTGRESプロジェクト開始 | Michael StonebrakerがUC Berkeleyで開始、Ingresに触発 |
| 1995 | Postgres95リリース | SQL言語サポートを追加(PostQUELクエリ言語を置き換え) |
| 1996 | PostgreSQL 6.0 | 正式にPostgreSQLに改名、オープンソースとしてリリース |
| 2005 | バージョン8.0 | ネイティブWindowsサポート、セーブポイント、二相コミット |
| 2012 | バージョン9.2 | JSONサポート(9.4でJSONBにアップグレード)、範囲型 |
| 2016 | バージョン9.6 | 並列クエリ、フレーズ全文検索 |
| 2017 | バージョン10.0 | 宣言的パーティショニング、論理レプリケーション、SCRAM認証 |
| 2022 | バージョン15.0 | MERGEコマンド(SQL標準UPSERT) |
| 2024 | バージョン17.0 | 現行LTSリリース、論理レプリケーション強化、完全なSQL/JSON標準 |
(2) PostgreSQLの設計思想
PostgreSQLの核となる設計思想は、次の4つの言葉に集約できます:
| 原則 | 具体化されている点 |
|---|---|
| 標準準拠 | SQL標準(SQL:2023)に厳格に準拠し、ほとんどの標準機能をサポート |
| 拡張性 | カスタム型、関数、インデックスメソッド、手続き言語をサポート(Extensions経由) |
| 信頼性 | ACIDトランザクション、MVCC同時実行制御、WAL先行書き込みログ — デフォルトでデータが失われない |
| コミュニティ主導 | 単一企業の支配を受けず、世界中の1000人以上のコントリビューターが参加、真のオープンソース |
5. PostgreSQL vs MySQL
これはAliceと彼女のチームが最も気にしていたことです。以下に複数の観点からの客観的な比較を示します:
| 観点 | PostgreSQL | MySQL |
|---|---|---|
| アーキテクチャ | プロセスモデル(1接続1プロセス) | スレッドモデル(1接続1スレッド) |
| 同時実行性 | MVCC(マルチバージョン同時実行制御):読み取りと書き込みが互いにブロックしない | 主にテーブルロック、InnoDBは行ロックだが範囲は限定的 |
| SQL標準 | SQL:2023に厳格準拠 | 部分準拠、MySQL固有の構文が多い |
| JSONサポート | JSONB(バイナリス��レージ、インデックス検索が非常に高速) | JSON(テキストストレージ、機能は限定的) |
| 全文検索 | 組み込みのtsvector/tsquery、多言語サポート | FULLTEXTインデックス、基本機能 |
| インデックス型 | 6種類(B-Tree / GIN / GiST / BRIN / SP-GiST / Hash) | 3種類(B-Tree / Hash / Fulltext) |
| 拡張エコシステム | CREATE EXTENSIONでワンステップインストール(pgvector / PostGISなど) | 同等の仕組みなし |
| パーティショニング | 宣言的パーティショニング(RANGE / LIST / HASH、PG 10+) | パーティションテーブル(8.0+)、構文が冗長 |
| レプリケーション | ストリーミング+論理レプリケーション(テーブル単位でレプリケート) | マスタースレーブレプリケーション(インスタンス全体) |
| バックアップ | PITRポイントインタイムリカバリ(秒単位) | binlogリプレイ(粗い粒度) |
| ライセンス | PostgreSQLライセンス(BSDライク、非常に寛容) | GPL(商用制限あり) |
| 複雑なクエリ | ウィンドウ関数 / 再帰CTE / LATERAL JOINをネイティブサポート | 8.0+でサポートされるがPGより弱い |
| 運用 | 多くの設定オプション、チューニングの余地が大きい | すぐに使える、運用がシンプル |
| 市場シェア | DB-Enginesで7年連続最も成長しているデータベース | 世界最大のインストールベース |
▶ サンプル:UPSERT構文の比較
-- PostgreSQL:INSERT ON CONFLICT(より柔軟)
INSERT INTO users (email, name)
VALUES ('alice@example.com', 'Alice')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name, updated_at = NOW()
RETURNING id, name, updated_at;
-- RETURNING句は影響を受けた行を返す(MySQLには同等のものがない)
Output:
INSERT 0 1
-- MySQL:ON DUPLICATE KEY UPDATE(柔軟性が低い)
INSERT INTO users (email, name)
VALUES ('alice@example.com', 'Alice')
ON DUPLICATE KEY UPDATE name = VALUES(name), updated_at = NOW();
-- RETURNING句がないため、結果を得るには別途SELECTが必要
▶ サンプル:JSONBクエリの比較
-- PostgreSQL:GINインデックス付きJSONB(高速なインデックス検索)
SELECT name FROM products
WHERE attributes @> '{"color": "red"}'::jsonb;
-- @> は「含む」演算子、GINインデックスを使用
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
-- MySQL:JSON関数(このパターンではインデックスサポートなし)
SELECT name FROM products
WHERE JSON_CONTAINS(attributes, '"red"', '$.color');
-- インデックスなし、▶ルテーブルスキャン
▶ サンプル:全文検索の比較
-- PostgreSQL:ランキング付き組み込み全文検索
SELECT name, ts_rank(to_tsvector('english', name || ' ' || description),
to_tsquery('english', 'red & shoes')) AS rank
FROM products
WHERE to_tsvector('english', name || ' ' || description)
@@ to_tsquery('english', 'red & shoes')
ORDER BY rank DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
-- MySQL:基本的なFULLTEXT検索(ランキングの柔軟性なし)
SELECT name FROM products
WHERE MATCH(name, description) AGAINST('red shoes' IN BOOLEAN MODE);
6. PostgreSQLの主要な強み
(1) ACIDトランザクション
ACIDはデータの信頼性を保証する4つの特性です:
| 特性 | 正式名称 | 意味 | PostgreSQLの実装 |
|---|---|---|---|
| A | Atomicity(原子性) | オール・オア・ナッシング:トランザクションは完全に成功するか完全にロールバックする | WAL先行書き込みログ |
| C | Consistency(一貫性) | トランザクションの前後でデータベースは有効な状態を保つ | 制約 / トリガー / 型チェック |
| I | Isolation(分離性) | 同時実行トランザクションが互いに干渉しない | MVCCマルチバージョン同時実行制御 |
| D | Durability(永続性) | コミットされたデータは決して失われない | WAL + fsync |
(2) MVCCマルチバージョン同時実行制御
MVCCはPostgreSQLの同時実行性の核心です。読み取りは書き込みをブロックせず、書き込みは読み取りをブロックしません:
| シナリオ | MySQL(InnoDB) | PostgreSQL(MVCC) |
|---|---|---|
| 読み書きの同時実行 | 共有/排他ロック、ブロックの可能性あり | 読み取りはスナップショットを見る、書き込みは新しいバージョンを作成 — ブロックなし |
| 長時間トランザクションの影響 | 他のトランザクションをブロック | ブロックなし、古いバージョンのみ保持 |
| 一貫性読み取り | MVCCが必要だが実装が複雑 | 自然なスナップショット分離 |
(3) 拡張性
PostgreSQLの拡張メカニズム(CREATE EXTENSION)は最大のエコシステムの強みです:
| 拡張 | 機能 | 置き換えるミドルウェア |
|---|---|---|
| pgvector | ベクトル検索 / AIエンベディング | Pinecone / Weaviate |
| PostGIS | 地理空間クエリ | MongoDB Geo |
| pg_trgm | あいまい検索 | Elasticsearch |
| pgcrypto | 暗号化関数 | アプリケーション層の暗号化 |
| uuid-ossp | UUID生成 | アプリケーション層の生成 |
| postgres_fdw | クロスデータベースクエリ | ETLツール |
▶ サンプル:拡張機能のインストールと使用
-- 利用可能な拡張を確認
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name IN ('uuid-ossp', 'pg_trgm', 'pgcrypto');
-- 拡張をインストール
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 拡張を使ってUUIDを生成
SELECT uuid_generate_v4();
-- 出力:550e8400-e29b-41d4-a716-446655440000 のような一意のUUID
Output:
CREATE TABLE
▶ サンプル:FILTERによる条件付き集計
-- PostgreSQL:条件付き集計のためのFILTER句(SQL標準)
SELECT
store_id,
COUNT(*) AS total_orders,
COUNT(*) FILTER (WHERE status = 'completed') AS completed_orders,
COUNT(*) FILTER (WHERE status = 'cancelled') AS cancelled_orders
FROM orders
GROUP BY store_id;
Output:
count
-------
5
(1 row)
-- MySQL:SUM(IF()) または SUM(CASE) の回避策が必要
SELECT
store_id,
COUNT(*) AS total_orders,
SUM(IF(status = 'completed', 1, 0)) AS completed_orders,
SUM(IF(status = 'cancelled', 1, 0)) AS cancelled_orders
FROM orders
GROUP BY store_id;
7. PostgreSQLの一般的なユースケース
| シナリオ | PGの機能 | 例 |
|---|---|---|
| Eコマース | JSONB商品属性 + 全文検索 + パーティショニング | 動的なSKU属性、商品検索、月次パーティション注文 |
| SaaSマルチテナント | RLS行レベルセキュリティ + スキーマ分離 | 1つのデータベースで多数のテナントにサービス、行レベルポリシーでデータ分離 |
| データ分析 | ウィンドウ関数 + マテリアライズドビュー + CTE | ユーザーリテンション分析、売上トレンドレポート、複雑な統計 |
| AI / レコメンデーション | pgvectorベクトル検索 | 商品類似度、セマンティック検索、RAGアプリ |
| GISサービス | PostGIS拡張 | 地図アプリ、距離計算、ルート計画 |
| 金融 | ACID + 3レベルの分離 + PITR | 送金、照合、ポイントインタイムリカバリ |
| コンテンツ管理 | 全文検索 + JSONB | 記事検索、タグ管理、柔軟なコンテンツ |
▶ サンプル:Eコマース — 商品属性のJSONB活用
-- 商品ごとに異なる属性がある場合
-- 属性タイプごとに個別のテーブルや列は不要
INSERT INTO products (name, price, attributes) VALUES
('ランニングシューズ', 89.99, '{"color": "red", "size": 42, "weight_grams": 280}'::jsonb),
('ノートパソコン', 1299.00, '{"cpu": "M3", "ram_gb": 16, "screen_inch": 14}'::jsonb),
('コーヒー豆', 24.50, '{"origin": "Colombia", "roast": "medium", "weight_kg": 1}'::jsonb);
-- クエリ:100ドル未満の赤色の商品をすべて検索
SELECT name, price, attributes
FROM products
WHERE price < 100
AND attributes @> '{"color": "red"}'::jsonb;
Output:
name | price | attributes
---------------+--------+--------------------------------------------
ランニングシューズ | 89.99 | {"color": "red", "size": 42, "weight_grams": 280}
8. 完全な例:データベース選定の決定フロー
-- ============================================
-- 総合的な例:PostgreSQLの機能デモ
-- PGの1つのデータベースが複数のツールを置き換えられる理由を示す
-- ============================================
-- 1. ACIDトランザクション(アプリケーション層の整合性処理が不要)
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
COMMIT;
-- 2. JSONBストレージ(MongoDB不要)
INSERT INTO products (name, attributes)
VALUES ('スマートウォッチ', '{"color": "black", "battery_life_hours": 48, "waterproof": true}'::jsonb);
-- 3. 全文検索(Elasticsearch不要)
SELECT name, ts_rank(
to_tsvector('english', name),
plainto_tsquery('english', 'smart watch')
) AS relevance
FROM products
WHERE to_tsvector('english', name) @@ plainto_tsquery('english', 'smart watch')
ORDER BY relevance DESC;
-- 4. ウィンドウ関数(アプリケーション層のランキング処理が不要)
SELECT name, price,
RANK() OVER (ORDER BY price DESC) AS price_rank
FROM products;
-- 5. UPSERT(アプリケーション層の競合処理が不要)
INSERT INTO products (name, price, attributes)
VALUES ('スマートウォッチ', 199.99, '{"color": "black", "battery_life_hours": 48}'::jsonb)
ON CONFLICT (name)
DO UPDATE SET price = EXCLUDED.price,
attributes = EXCLUDED.attributes
RETURNING id, name, price;
Output(抜粋):
-- UPSERT RETURNING の出力:
id | name | price
----+-------------+--------
1 | スマートウォッチ | 199.99
❓ よくある質問
📖 まとめ
- PostgreSQLは世界で最も先進的なオープンソースリレーショナルデータベースであり、SQL標準に厳格に準拠しています
- PGは1986年のPOSTGRESから2024年のPostgreSQL 17へと進化し、継続的に改善されています
- PG vs MySQL:PGは複雑なクエリ、JSONB、全文検索、拡張エコシステム、データ整合性でリードし、MySQLはシンプルなシナリオと運用の容易さで勝ります
- PGの主要な強み:ACIDトランザクション、MVCC同時実行制御、拡張メカニズム、JSONB、全文検索
- 1つのPGデータベースで複数のミドルウェアコンポーネント(MongoDB + Elasticsearch + Redis)を置き換え、運用の複雑さを軽減できます
- PGのユースケース:Eコマース、SaaS、データ分析、AIレコメンデーション、GIS、金融、コンテンツ管理
📝 練習問題
-
基礎(★): MySQLと比較してPostgreSQLをユニークにしている3つの機能(MySQLにないか大幅に劣る機能)を挙げ、それぞれの利点を一文で説明してください。
-
中級(★★): コース情報(構造化)と学生のノート(非構造化)を保存し、コース内容を検索する必要があるオンライン教育プラットフォームのデータベースを選定するとします。PostgreSQLまたはMySQLを選ぶ理由を、少なくとも3つの技術的特徴を挙げて論じてください。
-
発展(★★★): PostgreSQLドキュメントの「Feature Matrix」ページ(「PostgreSQL Feature Matrix」で検索)を読み、このレッスンで触れられていないPGの機能を3つ見つけ、それぞれがどの外部ツールやミドルウェアを置き換えられるかを説明してください。