目次
新しいシステムのDBを決めるとき、「MySQLとPostgreSQL、結局どちらがいいのか」という問いに明確に答えられる人は意外と少ないものです。「前のプロジェクトがMySQLだったから」「フレームワークのデフォルトがPostgreSQLだったから」という理由で決まり、あとから「JSONを本格的に使いたい」「レプリカを増やしたい」といった場面で違いに気づくことも珍しくありません。
この記事では、両者をライセンスとエコシステム、トランザクションとMVCCの実装、JSON・全文検索・拡張機能、レプリケーション方式、クラウドのマネージド対応、そして移行時の落とし穴という6つの観点で比較します。読み終えるころには、自社の要件に対してどちらを選ぶべきか、そしてすでに使っている側から乗り換えるべきかどうかを、自分の言葉で説明できるようになるはずです。なお、NoSQLとの比較はMongoDB vs PostgreSQL、PostgreSQLの基本操作はPostgreSQL入門で扱っているため、本記事ではRDBMS同士の違いに絞ります。
MySQLとPostgreSQLの成り立ちとライセンスの違い
開発体制とライセンス
MySQLはOracle社が開発を主導し、GPLv2と商用ライセンスのデュアルライセンスで提供されています。アプリからネットワーク越しに接続して使う限りGPLの影響は実質ありませんが、ライブラリを組み込んで配布する製品では商用ライセンスの検討が必要になります。Oracle買収時に派生したMariaDBは互換性を保ちつつ独自路線を歩んでおり、「MySQL互換」といっても細部の機能差が広がっている点は把握しておくべきです。
PostgreSQLはコミュニティが開発し、BSDに近い緩やかなPostgreSQL Licenseで提供されます。特定企業の方針に左右されにくく、商用製品への組み込みも自由です。年1回のメジャーリリースと5年間のサポートというサイクルが安定して続いています。
エコシステムと採用の傾向
MySQLはWordPressなどPHP系CMSやレンタルサーバーでの実績が厚く、PostgreSQLはRails・Django・Prisma・Supabaseといった近年のフレームワークやBaaSがデフォルトに据えることが多くなっています。「周囲に相談できる人がどちらに詳しいか」も、技術的な優劣と同じくらい重要な判断材料です。
トランザクション・ロック・MVCC実装の違い
同じMVCCでも仕組みが異なる
両者ともMVCC(多版型同時実行制御。読み取りと書き込みが互いをブロックしないよう、行の複数バージョンを保持する仕組み)を採用していますが、古いバージョンの置き場所が違います。MySQL(InnoDB)は古い行をundoログという別領域に退避し、不要になれば自動で回収します。PostgreSQLは古い行をテーブル本体(ヒープ)にそのまま残し、VACUUMが後から回収します。
この違いは運用に直結します。PostgreSQLでは長時間開きっぱなしのトランザクションがあると、その間にできた古い行を回収できず、テーブルが肥大化(ブロート)して性能が落ちます。自動VACUUMが標準で動くとはいえ、更新の激しいテーブルではpg_stat_user_tablesでn_dead_tupを監視し、autovacuumのパラメータを調整する運用知識が求められます。
デフォルトの分離レベルとロックの挙動
デフォルトのトランザクション分離レベルは、MySQL(InnoDB)がREPEATABLE READ、PostgreSQLがREAD COMMITTEDです。同じアプリコードでも「同一トランザクション内で2回SELECTした結果が変わるかどうか」が異なるため、移行時に不具合として顕在化することがあります。またInnoDBのREPEATABLE READにはギャップロック(範囲に対するロック)があり、INSERTが競合してデッドロックになるケースはMySQL特有です。分離レベルとデッドロックの詳細はトランザクション分離レベルとデッドロックで解説しています。
-- 現在の分離レベルを確認する
-- MySQL
SELECT @@transaction_isolation;
-- PostgreSQL
SHOW default_transaction_isolation;
-- どちらもトランザクション単位で変更できる
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
JSON・全文検索・拡張機能の比較
JSONの扱い
PostgreSQLのjsonb型はバイナリ形式で格納され、GINインデックスを張ることで「このキーにこの値を含む行」を高速に検索できます。演算子(@>、->>など)も豊富で、半構造化データをRDBの中で本格的に扱う用途に向いています。MySQLもJSON型を持ち、生成列(generated column)にインデックスを張る方法や、8.0以降のマルチバリューインデックスで検索を高速化できますが、設計の自由度はPostgreSQLのほうが高いと感じる場面が多いでしょう。
-- PostgreSQL: jsonb列にGINインデックスを作り、包含検索する
CREATE INDEX idx_orders_meta ON orders USING GIN (meta);
SELECT * FROM orders WHERE meta @> '{"channel": "web"}';
-- MySQL: JSONから生成列を作ってインデックスを張る
ALTER TABLE orders
ADD COLUMN channel VARCHAR(32)
GENERATED ALWAYS AS (meta->>'$.channel') STORED,
ADD INDEX idx_orders_channel (channel);
日本語の全文検索
どちらも組み込みの全文検索機能を持ちますが、日本語は単語の区切りがないため標準のままでは実用になりません。MySQLはFULLTEXTインデックスにngramパーサーを指定することで日本語に対応できます。PostgreSQLはpg_bigmやPGroongaといった拡張を入れるのが一般的で、特にPGroongaは形態素解析に近い精度が出せます。マネージドサービスでは使える拡張が限られるので、この点は事前に確認が必要です。
拡張機能の広がり
PostgreSQLの大きな強みが拡張機能(Extension)です。地理情報のPostGIS、ベクトル検索のpgvector、時系列向けのTimescaleDBなど、本体を改造せずに機能を追加できます。「まずPostgreSQLで始めて、必要になったら拡張で広げる」という戦略が取りやすいのは、専用のミドルウェアを増やしたくない小規模チームにとって現実的な利点です。
レプリケーションとマネージドサービスの対応
レプリケーション方式の違い
| 観点 | MySQL | PostgreSQL |
|---|---|---|
| 基本方式 | バイナリログ(binlog)を転送する論理レプリケーション | WALを転送するストリーミング(物理)レプリケーション |
| 論理レプリケーション | 標準(行ベース/文ベース) | 10以降で標準。テーブル単位の選択が可能 |
| 異なるバージョン間 | 比較的柔軟 | 物理は同一メジャーバージョン必須、論理なら可 |
| クラスタリング | Group Replication、InnoDB Cluster | Patroni等の外部ツールで構成するのが一般的 |
MySQLは論理レプリケーションが標準で柔軟な構成が取りやすく、PostgreSQLの物理レプリケーションはプライマリと完全に同一のコピーを作るため単純で信頼性が高く、読み取り専用レプリカとしてそのまま使えます。読み取り分散や遅延の扱いはデータベースレプリケーション入門で詳しく扱います。
クラウドでの選択肢
AWSのRDSとAurora、GCPのCloud SQL、AzureのDatabase for MySQL/PostgreSQLは、いずれも両方に対応しています。機能差より「使える拡張機能」「メジャーバージョンアップの手順」「料金体系」を比較するほうが実務的です。RDSでの構築や運用はAWS RDSの実務ガイドを参照してください。Supabase・NeonのようにPostgreSQL専業のサービスもあり、使いたいサービスがどちらに寄っているかも選定に影響します。
移行時の落とし穴と判断基準
MySQLからPostgreSQLへ移す際にハマる差異
- 識別子の大文字小文字:PostgreSQLは引用符なしの識別子を小文字に畳みます。
CREATE TABLE "Users"と作ってからSELECT * FROM Usersと書くと「relation does not exist」になります。 - 文字列比較:MySQLのデフォルト照合順序は大文字小文字を区別しませんが、PostgreSQLは区別します。ログイン処理などでメールアドレスの一致判定が変わります。
- UPSERT構文:
INSERT ... ON DUPLICATE KEY UPDATEはINSERT ... ON CONFLICT (col) DO UPDATEに書き換えが必要です。 - 自動採番と真偽値:
AUTO_INCREMENTはGENERATED ... AS IDENTITYへ、tinyint(1)で代用していた真偽値はboolean型へ変換します。 - ゼロ日付:MySQLで許容されてきた
'0000-00-00'はPostgreSQLに投入できません。事前にNULLへ変換が必要です。
データ移行はpgloaderで型変換込みで自動化できますが、アプリ側のSQLの書き換えとテストが工数の大半を占めます。
# pgloaderでMySQLからPostgreSQLへスキーマとデータを移行する例
pgloader mysql://app:pass@10.0.0.5/shop \
postgresql://app:pass@10.0.0.6/shop
トラブル事例:移行後にテーブルが肥大化して遅くなった
症状:MySQLからPostgreSQLに移行して数週間後、更新の多い注文テーブルのクエリが日に日に遅くなり、ディスク使用量も増え続けた。
原因:バッチ処理が長時間トランザクションを張ったまま動いており、その間の古い行バージョンをautovacuumが回収できずブロートが進んでいた。MySQL時代はundoログが自動回収されるため同じコードで問題が出ておらず、MVCC実装の違いが表面化した形です。
対処:pg_stat_activityで長時間のトランザクションを特定してバッチを小さなトランザクションに分割し、対象テーブルのautovacuum_vacuum_scale_factorを下げてVACUUMの頻度を上げました。肥大化した分はpg_repackでオンラインのまま解消しています。
どちらを選ぶかの判断基準
- MySQLが向くケース:WordPressやPHP系CMSが中心、レンタルサーバーや既存のMySQL資産が多い、単純な読み取り中心のWebアプリ、チームの経験がMySQLに偏っている
- PostgreSQLが向くケース:JSONや地理情報・ベクトル検索など多様なデータを1つのDBで扱いたい、複雑なSQL(ウィンドウ関数・CTE・部分インデックス)を積極的に使う、Rails・Django・Prisma・Supabaseなど周辺がPostgreSQL前提
- どちらでもよいケース:一般的なCRUDアプリ。この場合は「運用できる人がいるほう」「使うマネージドサービスで安く済むほう」で決めて問題ありません
まとめ
MySQLとPostgreSQLは、どちらも成熟した信頼できるRDBMSであり、一般的な業務システムではどちらを選んでも致命的な差は出ません。違いが効いてくるのは、MVCC実装に起因するVACUUM運用、JSONや拡張機能で守備範囲を広げたいとき、レプリケーションの方式を選ぶとき、そして移行するときです。新規開発でこだわりがなければPostgreSQLを軸に検討し、既存資産やチームの経験がMySQLに寄っていれば無理に変えない、というのが現実的な結論になります。まずは自分のプロジェクトで「JSONをどれだけ使うか」「読み取りレプリカが必要か」「誰が運用するか」の3点を書き出してみてください。
DB選定からスキーマ設計、クラウド移行まで判断に迷う場合は、Harmonic Societyのシステム開発・インフラ支援にお気軽にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します