MySQL vs PostgreSQL:Webアプリ開発で使うデータベースの違いと選び方
目次
MySQLとPostgreSQLの概要
MySQLは1995年、PostgreSQLは1996年に最初のリリースを迎えた老舗のオープンソースRDBMSです。現在も両者はWebアプリ開発の現場でトップシェアを争い続けています。
| 項目 | MySQL 8.4 | PostgreSQL 17 |
|---|---|---|
| 初版 | 1995年 | 1996年 |
| ライセンス | GPL v2(Community版) / 商用 | PostgreSQL License(BSDライク) |
| 開発元 | Oracle(買収後) | PostgreSQL Global Development Group |
| ACID準拠 | ✓(InnoDBストレージエンジン) | ✓(完全対応) |
| SQLの標準準拠度 | 中程度 | 高い |
| 最大DB容量 | 無制限 | 無制限 |
アーキテクチャの違い
MySQLのストレージエンジン
MySQLはストレージエンジンという仕組みでデータの保存方式を差し替えられます。現在の実質的なデフォルトはInnoDBで、ACID準拠・行レベルロック・外部キー制約に対応しています。
古いMyISAMはトランザション非対応のため、新規開発ではInnoDBしか選択肢がありません。
-- テーブル作成時にストレージエンジンを指定(デフォルトはInnoDB)
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PostgreSQLの拡張型システム
PostgreSQLはカスタム型・演算子・インデックスメソッドを定義できる強力な拡張システムを持ちます。CREATE EXTENSIONで追加できる代表的な拡張が以下です。
-- pg_trgm: 正規表現・あいまい検索を高速化
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- uuid-ossp: UUID生成関数
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- PostGIS: 地理空間データの処理(位置情報アプリに必須)
CREATE EXTENSION IF NOT EXISTS postgis;
PostgreSQLはCREATE TYPEでENUM以外にも独自の複合型・ドメイン型を定義できます。
JSON対応の比較
MySQLのJSON型
MySQL 5.7以降でJSON型が追加されました。バリデーションはあるものの、演算子や関数はPostgreSQLより少なめです。
-- MySQL: JSON型カラム
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
attributes JSON
);
INSERT INTO products (name, attributes)
VALUES ('スニーカー', '{"size": 26, "color": "white", "tags": ["casual", "sports"]}');
-- JSON値の参照
SELECT name, attributes->>'$.color' AS color FROM products;
-- JSON配列の検索
SELECT name FROM products
WHERE JSON_CONTAINS(attributes, '"casual"', '$.tags');
PostgreSQLのJSONBとGIN
PostgreSQLにはjsonとjsonbの2種類があります。jsonbはバイナリ形式で保存し、GINインデックスを使って高速に検索できるため、実用ではjsonb一択です。
-- PostgreSQL: JSONB型
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
attributes JSONB
);
INSERT INTO products (name, attributes)
VALUES ('スニーカー', '{"size": 26, "color": "white", "tags": ["casual", "sports"]}');
-- GINインデックスで検索を高速化
CREATE INDEX idx_products_attrs ON products USING GIN (attributes);
-- 演算子で検索(含むかどうか)
SELECT name FROM products
WHERE attributes @> '{"color": "white"}';
-- 配列内を検索
SELECT name FROM products
WHERE attributes->'tags' ? 'casual';
-- JSONB_PATH_QUERY(JSON Path)
SELECT name FROM products
WHERE jsonb_path_exists(attributes, '$.tags[*] ? (@ == "sports")');
JSONを多用するAPIでは、PostgreSQLのJSONBとGINインデックスが圧倒的に高機能です。
パフォーマンス特性
読み取り重視なら MySQL が有利な場合がある
MySQLはシンプルなSELECTクエリに特化した最適化がされており、Webアプリの典型的な「読み取り多・書き込み少」のワークロードで高いスループットを発揮します。
複雑なクエリはPostgreSQLが得意
JOINが多い複雑なクエリ・ウィンドウ関数・CTEの最適化はPostgreSQLが優れています。
-- PostgreSQL: WITH RECURSIVE(再帰CTE)でツリー構造を辿る
WITH RECURSIVE category_tree AS (
-- ベースケース
SELECT id, name, parent_id, 1 AS depth
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- 再帰ケース
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY depth, name;
MySQLも再帰CTEに対応していますが、PostgreSQLの方がクエリプランナーの最適化が優れているケースが多いです。
レプリケーション
MySQL
PRIMARY → REPLICA構成のレプリケーションは歴史が長く、実績があります。グループレプリケーションを使うと複数ノードへの同期書き込みが可能で、PlanetScaleはこれを基盤としています。
-- MySQLのレプリケーション確認
SHOW REPLICA STATUS\G
PostgreSQL
論理レプリケーション(PostgreSQL 10以降)でテーブル単位のサブスクリプションが可能です。またStreaming Replicationは非同期・同期両対応で、Supabaseのフェイルオーバーに使われています。
ライセンスと商用利用
| 項目 | MySQL Community | PostgreSQL |
|---|---|---|
| ライセンス | GPL v2 | PostgreSQL License |
| 商用製品への組み込み | 別途商用ライセンス必要 | 制限なし |
| OracleによるSaaSへの組み込み | 要確認 | 自由 |
| フォーク | MariaDB, Percona Server等 | なし(単一コードベース) |
PostgreSQLは組み込み・商用利用の制限がなく、ライセンスリスクがゼロです。SaaS製品に組み込む場合はPostgreSQLの方が安心です。
主要ホスティングサービスとの相性
| サービス | 対応DB | 特徴 |
|---|---|---|
| PlanetScale | MySQL互換(Vitess) | スキーマ変更が安全・ブランチ機能あり |
| Supabase | PostgreSQL | リアルタイム・Auth・Storage込み |
| Neon | PostgreSQL | Serverless・ブランチ・自動スケール |
| AWS RDS | MySQL / PostgreSQL 両対応 | マネージド・マルチAZ対応 |
| AWS Aurora | MySQL互換 / PostgreSQL互換 | RDSの最大5倍の読み取りスループット |
| Railway | MySQL / PostgreSQL 両対応 | 簡単デプロイ・開発向け |
| Render | PostgreSQL | 無料プランあり |
フロントエンドと一緒にデプロイするスタックならSupabase(PostgreSQL)かPlanetScale(MySQL互換)の二択になる場合が多いです。
プロジェクト規模別の選定基準
小規模・個人開発・スタートアップ初期
おすすめ: Supabase(PostgreSQL)
- 無料プランで十分
- Auth・Storage・Realtime機能が標準装備でバックエンドを減らせる
- PostgreSQLの全機能が使える
# Supabase CLI でローカル環境を起動
npx supabase init
npx supabase start
中規模・Webアプリ(月間PV 10万〜100万程度)
おすすめ: AWS RDS(MySQL or PostgreSQL)
- マネージドで運用コストが低い
- マルチAZでフェイルオーバー対応
- 複雑なビジネスロジックがあるならPostgreSQL
大規模・高トラフィック(月間PV 1000万以上)
おすすめ: Aurora MySQL or Aurora PostgreSQL
- RDSの最大5倍の読み取りスループット
- リードレプリカを最大15台まで追加可能
- グローバル対応(Aurora Global Database)
選定フローチャート
- JSONデータを多用する? → PostgreSQL(JSONB + GINインデックスが強力)
- 地理空間データを扱う? → PostgreSQL(PostGIS拡張)
- 既存チームがMySQLに慣れている? → MySQL(学習コスト重視)
- Supabaseのエコシステムを使いたい? → PostgreSQL
- PlanetScaleのブランチ機能を使いたい? → MySQL互換
理論から学ぶデータベース実践入門 ―リレーショナルモデルによる効率的なSQL
MySQLを例にSQLとリレーショナルモデルの理論を体系的に解説。正規化・インデックス・トランザクションを原理から理解したいエンジニアに最適です。
※ アフィリエイトリンクを含みます
PostgreSQL全機能バイブル
PostgreSQLの基礎から高度な機能(JSONB・PostGIS・レプリケーション)まで網羅した決定版リファレンス。DBAからアプリ開発者まで使える実践的な内容です。
※ アフィリエイトリンクを含みます
まとめ
MySQLとPostgreSQLはどちらも成熟した優れたRDBMSです。最終的な選定は以下のポイントで判断しましょう。
- PostgreSQLを選ぶ: 複雑なクエリ・JSON多用・地理空間・ライセンスリスクを避けたい・Supabase利用
- MySQLを選ぶ: チームの習熟度・PlanetScale利用・シンプルなCRUD主体のWebアプリ
どちらのDBを使ったとしても、インデックス設計とクエリの最適化がパフォーマンスに最も大きく影響します。DBの選定と同じくらい、EXPLAINでクエリプランを読む習慣をつけることが重要です。