Linux管理者ならば一度は聞かれたことがあるはずだ。どちらも長年の実績を持つオープンソースRDBMSだが、アーキテクチャもレプリケーションの仕組みも根本から異なる。
「とりあえずMySQL」で問題ないケースは確かにある。だが、複雑な集計クエリや地理情報、JSONB型が要件に入った瞬間に、後から移行する羽目になる。この記事では、Linuxサーバーへのセルフホスト運用に絞り、選定判断に直結するアーキテクチャの差・レプリケーション方式の違い・向き不向きを実務目線で比較する。RHEL 9 / Ubuntu 24.04 LTSを想定している。
この記事のポイント
・PostgreSQLはMVCC+プロセスモデル、MySQLはスレッドモデルで根本から別物
・ギャップロックの有無がデッドロック発生パターンを左右する
・複雑な集計・JSONB・地理情報ならPostgreSQL、高速CRUD・大量接続はMySQL
・VACUUM管理と移行コストがセルフホスト選定で見落とされやすい注意点
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜアーキテクチャの違いが自前運用の判断を左右するのか
表面上はどちらも「SQLを話すRDBMS」として同じに見える。しかし内部構造が違うと、自前運用での挙動—トランザクション競合時の振る舞い、レプリカの遅延、チューニングパラメータの意味—が変わる。選定ミスに気づくのは、大抵は本番に入れた後だ。最初にアーキテクチャの核心を押さえておく。後のレプリケーション設計やトラブル対処の判断が、ここを理解しているかどうかで大きく変わる。
ストレージエンジンとMVCCの仕組みの違い
1. ストレージの設計:プラガブル型 vs ヒープ単一型
MySQLはストレージエンジンをプラガブル(着脱可能)な設計で持っている。InnoDB(トランザクション対応・デフォルト)、MyISAM(高速読み取り・トランザクション非対応)などから選択できるが、現在の実運用ではInnoDB一択が事実上の標準だ。PostgreSQLにはストレージエンジンの概念がなく、単一のヒープベースストレージを使う。すべてのテーブルは内部でヒープファイル(行レコードの集合)として管理される。この違いが、VACUUM運用の必要性に直結する。
・MySQL InnoDB:不要になった行はundo logに残り、パージスレッドが非同期に回収する。管理者が明示的に実行する命令はない
・PostgreSQL:更新・削除後のデッドタプル(古い行バージョン)はヒープファイルに残り続ける。VACUUMまたはautovacuumで定期回収しないと、テーブルが肥大化してXID周回(トランザクションID枯渇)を引き起こす
2. PostgreSQLのプロセスモデルとMVCC
PostgreSQLはクライアント接続ごとに独立したバックエンドプロセスをfork()で生成する。プロセスモデルのため、コネクション数が増えるとOSレベルのプロセス管理コストが増える。100接続を超える環境では、pg_bouncerなどのコネクションプーラーが実務では必須に近い。トランザクション制御にはMVCC(Multi-Version Concurrency Control:マルチバージョン同時実行制御)を採用している。行を更新・削除しても旧バージョンをヒープ上に物理的に残し、後でVACUUMで回収する設計だ。このため「読み取りが書き込みをブロックしない」という特性が強く、SELECT多めのワークロードに向く。ただし更新・削除が頻繁なテーブルはVACUUMが追いつかないとテーブルが肥大化する点に注意が必要だ。
データ型の豊富さもPostgreSQLの特徴だ。配列型・JSONB型・範囲型・地理情報型(PostGISで拡張)など、標準のRDBMSとは異色の幅広さを持つ。
3. MySQLのスレッドモデルとInnoDB
MySQLはクライアント接続をスレッドで処理する。プロセスよりコンテキストスイッチが軽いため、同時接続数が増えてもPostgreSQLほどメモリを消費しない。PHP + MySQLという組み合わせがWebアプリで長年デファクトになってきた背景はここにある。ストレージエンジンはInnoDB(デフォルト)を使う。行レベルロックと独自MVCCを持つが、PostgreSQLとは内部実装が異なる。MySQLのMVCCはUndoログを使って旧バージョンを参照する設計で、PostgreSQLのようなVACUUM処理が不要な代わりに、Undoログの肥大化(ibdata1の膨張)には注意が必要だ。
4. ロック機構の違い:ギャップロックとデッドロック
MVCCの実装に加えて、ロック機構の設計も両者で大きく異なる。この差が、本番環境でのデッドロック発生パターンを左右する。| 比較項目 | PostgreSQL | MySQL InnoDB |
|---|---|---|
| MVCCの実装 | ヒープに旧バージョン行を保持(デッドタプル) | undo logセグメントに旧バージョンを保持 |
| デフォルト分離レベル | READ COMMITTED | REPEATABLE READ |
| ギャップロック | なし(直感的なロック挙動) | あり(ファントムリード防止・デッドロックの原因にも) |
| デッドロック発生傾向 | 比較的少ない | ギャップロック起因のデッドロックが発生しやすい |
| デッドタプルの回収 | VACUUMで回収(定期実行必須) | パージスレッドが自動回収 |
5. サービス起動とポートの確認
どちらを選んでも、自前運用の第一歩はサービスの起動確認だ。MySQLはデフォルトでTCP 3306番、PostgreSQLは5432番でリッスンする。# MySQL が起動してリッスンしているかを確認 [root@db-server ~]# ss -tlnp | grep 3306 LISTEN 0 151 0.0.0.0:3306 0.0.0.0:* users:(("mysqld",pid=2345,fd=21)) # PostgreSQL の場合 [root@db-server ~]# ss -tlnp | grep 5432 LISTEN 0 244 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1891,fd=5))
レプリケーション方式を比較する
自前運用で可用性を高めるには、レプリケーションの設定が避けられない。両者の仕組みと設定の手間を具体的に比較する。1. PostgreSQLのWALストリーミングレプリケーション
PostgreSQLのレプリケーションはWAL(Write-Ahead Log:先行書き込みログ)ファイルをスタンバイへ転送する方式だ。プライマリのpostgresql.confとpg_hba.conf、スタンバイ側のpostgresql.conf(PostgreSQL 12以降はstandby.signalファイルを配置)を設定する。プライマリ側の最低限の設定例(/etc/postgresql/16/main/postgresql.conf):
wal_level = replica max_wal_senders = 5 wal_keep_size = 256MB
[root@db-primary ~]# psql -U postgres -c \ "SELECT pid, usename, client_addr, state, sent_lsn, replay_lsn FROM pg_stat_replication;" pid | usename | client_addr | state | sent_lsn | replay_lsn ------+----------+---------------+-----------+-----------+------------ 3421 | repl_usr | 192.168.1.101 | streaming | 0/4A0B210 | 0/4A0B210 (1 row)
2. MySQLのGTIDレプリケーション
MySQLのレプリケーションはバイナリログ(binlog)を転送する方式で、GTID(Global Transaction ID:グローバルトランザクションID)を使った自動位置管理が現在の主流だ。ソース側の/etc/my.cnfへの追記例:
[mysqld] server-id = 1 log_bin = mysql-bin gtid_mode = ON enforce_gtid_consistency = ON
mysql> CHANGE REPLICATION SOURCE TO -> SOURCE_HOST='192.168.1.100', -> SOURCE_USER='repl_usr', -> SOURCE_PASSWORD='xxxxxxxx', -> SOURCE_AUTO_POSITION=1; mysql> START REPLICA;
mysql> SHOW REPLICA STATUS\G *************************** 1. row *************************** Replica_IO_Running: Yes Replica_SQL_Running: Yes Source_Host: 192.168.1.100 Seconds_Behind_Source: 0
3. 設定の難易度を実感として比較する
PostgreSQLのレプリケーションは設定項目が少なくシンプルだが、pg_basebackupでベースバックアップを取ってからスタンバイを起動する手順が初見では分かりにくい。フェイルオーバーを自動化したい場合は別途ツールの習得が必要だ。MySQLのGTIDレプリケーションは設定項目が多いものの、公式ドキュメントとコミュニティの情報が豊富でトラブル時に情報を探しやすい。MySQL RouterやOrchestatorなどのHA構成ツールとの組み合わせ事例も多く、エコシステムが充実している。
向き不向きのケース別判断
PostgreSQLが向いているケース
・複雑なJOINや窓関数(ウィンドウ関数)を多用する分析系クエリ・JSONB型で半構造化データをRDB内で柔軟に扱いたい
・PostGISで緯度経度・ポリゴンなどの地理情報を処理したい
・外部キー制約やCHECK制約・部分インデックス・関数インデックスを厳密に使いたい
・ストアドプロシージャをPL/pgSQL以外(Python・Perl・C)で書きたい
・pgvectorを使ったベクター検索など、PostgreSQL専用拡張を活用したい
MySQLが向いているケース
・PHP・Laravel・WordPressなど既存スタックでの実績がある・同時接続数が数百を超えるWebアプリでコネクションプールを極力減らしたい
・読み取りスケールのためにレプリカを複数台並べるシンプルな構成
・チーム内のMySQL経験者が多く、移行コストを最小化したい
・シンプルなCRUDが主体でORMを薄く保ちたい場合
・将来的にAWS RDS for MySQL / Aurora MySQLへの移行を想定している
どちらを選んでも、Linuxサーバー上でのレプリケーション設定・バックアップ運用・チューニングを体系的に習得することが長期的な安定運用の鍵になる。実機を触りながら習得したい方はLinux実践ハンズオンセミナーも選択肢に入れてほしい。
インストールと初期設定の違い
1. Rocky Linux / RHEL系での導入
MySQLは公式コミュニティリポジトリをDNFで追加してインストールする。初回起動時に一時パスワードが自動生成され、mysql_secure_installationで変更する流れだ。# MySQL Community リポジトリ追加(Rocky Linux 9 / RHEL 9) [root@db-server ~]# dnf install https://dev.mysql.com/get/mysql84-community-release-el9-1.noarch.rpm [root@db-server ~]# dnf install mysql-community-server [root@db-server ~]# systemctl enable --now mysqld [root@db-server ~]# grep 'temporary password' /var/log/mysqld.log 2026-09-09T09:12:03.421044Z 6 [Note] [MY-010454] [Server] A temporary password is generated \ for root@localhost: bXrDk7pf;mCL [root@db-server ~]# mysql_secure_installation
# PGDGリポジトリ追加(Rocky Linux 9 / RHEL 9) [root@db-server ~]# dnf install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm [root@db-server ~]# dnf -qy module disable postgresql [root@db-server ~]# dnf install postgresql16-server [root@db-server ~]# /usr/pgsql-16/bin/postgresql-16-setup initdb [root@db-server ~]# systemctl enable --now postgresql-16
2. サービス稼働状態の確認
インストール後はsystemctlでサービスの稼働状態を確認する習慣をつけておく。MySQLのサービス名はmysqldで固定だが、PostgreSQLはサービス名にバージョン番号が含まれる(postgresql-16)。複数バージョンを並行稼働できる設計のためだ。MySQLのサービス状態確認:
[root@db-server ~]# systemctl status mysqld * mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; preset: disabled) Active: active (running) since Thu 2026-09-09 09:25:44 JST; 3h ago Main PID: 1489 (mysqld) Status: Server is operational CGroup: /system.slice/mysqld.service +- 1489 /usr/sbin/mysqld
[root@db-server ~]# systemctl status postgresql-16 * postgresql-16.service - PostgreSQL 16 Database Server Loaded: loaded (/usr/lib/systemd/system/postgresql-16.service; enabled; preset: disabled) Active: active (running) since Thu 2026-09-09 09:23:11 JST; 3h ago Main PID: 1256 (postgres) CGroup: /system.slice/postgresql-16.service +- 1256 /usr/pgsql-16/bin/postgres -D /var/lib/pgsql/16/data/ +- 1257 postgres: logger +- 1258 postgres: checkpointer +- 1259 postgres: background writer +- 1261 postgres: autovacuum launcher
3. 初期接続とデータベース一覧の確認
起動確認が済んだら、CLIで接続してデータベース一覧を確認する。PostgreSQLはOSのpostgresユーザーに切り替えてからpsqlを起動する:
[root@db-server ~]# sudo -u postgres psql psql (16.3) Type "help" for help. postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres (3 rows)
[root@db-server ~]# sudo mysql -u root Welcome to the MySQL monitor. Commands end with ; or \g. Server version: 8.0.39 MySQL Community Server - GPL mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.01 sec)
4. Ubuntu / Debian系での導入
Ubuntuの場合、MySQLは`apt install mysql-server`一発で入り、初期設定もシンプルだ。PostgreSQLも`apt install postgresql-16`でインストールできるが、やはり`/etc/postgresql/16/main/pg_hba.conf`の設定が必要になる。Ubuntu環境ではpostgresパッケージが複数バージョンを管理する`pg_lsclusters`コマンドを使ってクラスター状態を確認できる。[root@db-server ~]# pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 16 main 5432 online postgres /var/lib/postgresql/16/main /var/log/postgresql/...
バックアップと復旧の手順の違い
論理バックアップは専用ツールを使う。MySQLはmysqldumpが定番で、行単位のSQL形式で出力する。PostgreSQLはpg_dump(1データベース)またはpg_dumpall(全データベース)を使う。| 操作 | MySQL | PostgreSQL |
|---|---|---|
| 論理バックアップ(全DB) | mysqldump --all-databases -u root -p |
pg_dumpall -U postgres |
| 論理バックアップ(1DB) | mysqldump -u root -p dbname |
pg_dump -U postgres dbname |
| リストア | mysql -u root -p dbname < dump.sql |
psql -U postgres dbname < dump.sql |
| 物理バックアップ | mysqlbackup / Percona XtraBackup | pg_basebackup -D /backup -Fp -Xs -P |
運用中に差が出るトラブルのパターン
「MySQLで動いているからそのまま移行しよう」のワナ
MySQLからPostgreSQLへの移行は、SQL方言の差が想定以上に大きい。代表的な変換が必要な箇所を押さえておく。・AUTO_INCREMENT → SERIAL(またはGENERATED ALWAYS AS IDENTITY):MySQLのAUTO_INCREMENTはPostgreSQLではSERIALまたはIDENTITY列に変換が必要だ
・TINYINT(1) → BOOLEAN:MySQLでbooleanの代用に使うTINYINT(1)はPostgreSQLではBOOLEAN型に変換する
・バックティック → ダブルクォート:MySQLの識別子クォートのバックティック(`)はPostgreSQLでは二重引用符(")に書き換える必要がある
・GROUP BYの厳格さの差:MySQLはGROUP BY非準拠のクエリを許可(sql_mode依存)するが、PostgreSQLは厳格にANSI SQL準拠を求める
数百本のSQLを書き直すコストが発生することがある。既存MySQLシステムの移行を検討するなら、事前にpgloaderなどで差分を洗い出すことを強く勧める。
VACUUMを放置すると起きること
PostgreSQLでUPDATE/DELETEが多いテーブルのVACUUMを放置すると、テーブルが肥大化してディスクを圧迫する。さらに32億トランザクションを超えると「XID周回(Transaction ID Wraparound)」と呼ばれる深刻な問題が起きる。autovacuumが正常に動いているかは定期的に確認する習慣をつけておく。# autovacuum の直近の動作ログを確認 [root@db-server ~]# grep "automatic vacuum" /var/log/postgresql/postgresql-2026-09-09_000000.log | tail -5 2026-09-09 08:15:02 JST [3821]: LOG: automatic vacuum of table "mydb.public.orders": index scans: 0 pages: 0 removed, 1024 remain, 8 outside target tuples: 5142 removed, 98756 remain, 0 are dead but not yet removable avg read rate: 2.1 MB/s, avg write rate: 0.4 MB/s
MySQLのmax_connectionsとコネクション枯渇
MySQLのデフォルトmax_connectionsは151(環境によって異なる)と低い。Webアプリのデプロイ時にコネクションプールの設定を見直さないまま本番に入れると、高負荷時に`Too many connections`エラーで詰まる。現在の接続数と上限はSHOW STATUSで確認できる。mysql> SHOW STATUS LIKE 'Threads_connected'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Threads_connected | 148 | +-------------------+-------+ mysql> SHOW VARIABLES LIKE 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 151 | +-----------------+-------+
よくある接続エラーと対処
初期セットアップ後に詰まりやすいエラーを2つ押さえておく。【「FATAL: role "root" does not exist」(PostgreSQL)】
Linuxのrootユーザーでそのままpsqlコマンドを実行したときに発生する。PostgreSQLはLinuxユーザー名に対応するロールでの認証を試みるため、`root`ロールが存在しないとこのエラーになる。`sudo -u postgres psql`でpostgresロールに切り替えてから接続する。
# NG: rootで直接実行 [root@db-server ~]# psql psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: role "root" does not exist # OK: postgresユーザーに切り替えてから実行 [root@db-server ~]# sudo -u postgres psql psql (16.3) postgres=#
MySQLの初期rootユーザーはauth_socket(Linuxユーザー認証)でロックされている。`sudo mysql`でパスワードなし接続するか、ALTER USERでパスワード認証に変更する。
# sudoを使えばパスワードなしで接続できる [root@db-server ~]# sudo mysql -u root # パスワード認証に変えたい場合(MySQLプロンプト内で実行) mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'StrongPass!123'; mysql> FLUSH PRIVILEGES;
本記事のまとめ
| 比較軸 | PostgreSQL | MySQL |
|---|---|---|
| プロセスモデル | コネクションごとにプロセスをfork(重い) | スレッドモデル(コネクション増加に強い) |
| MVCC実装 | 旧バージョン行をヒープに保持・VACUUMで回収 | Undoログで旧バージョンを参照・自動回収 |
| ロック機構 | ギャップロックなし・READ COMMITTED | ギャップロックあり・REPEATABLE READ |
| レプリケーション | WALストリーミング(設定が少なくシンプル) | binlog/GTID転送(情報豊富・ツール充実) |
| データ型 | 配列・JSONB・範囲型・地理情報など豊富 | 標準的なSQL型が中心 |
| バックアップ | pg_dump / pg_basebackup(PITR対応) | mysqldump / Percona XtraBackup |
| 向いているワークロード | 複雑な集計・JSONB・地理情報・厳密な制約 | 高速CRUD・大量接続・既存PHPスタック |
| 移行コスト | SQL方言の差が大きく既存資産の移行は重い | WordPress等との組み合わせ事例が豊富 |
| 注意点 | VACUUMとXID周回・pg_hba.confの設定 | max_connections・Undoログ肥大化 |
DBエンジンを選んだ後に差がつくのは、Linuxサーバー運用の「型」を知っているかどうか
PostgreSQLでもMySQLでも、DBエンジンの選定はスタートラインです。本番環境を安定して運用し続けるには、サービス設定・レプリケーション・バックアップの手順を体系的に身につけることが不可欠です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます