MySQL vs PostgreSQL:実機で学ぶモダンDB選定のポイント
mysql, postgresSQLをdocker コンテナを
準備
dockerのofficial imageを参考にmysql, postgresSQLそれぞれのコンテナを作成
services: postgres-db: image: postgres:18-alpine container_name: compare-postgres environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: test_db ports: - "5432:5432" mysql: image: mysql:9.0 container_name: compare-mysql environment: MYSQL_USER: user MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: test_db ports: - "3306:3306"
# 作成 docker-compose up # 確認 docker ps
非構造化データの扱い
モダンなアプリケーション開発では、タグ情報や動的なメタデータなど、JSON形式のデータを扱う機会が増えている。
PostgreSQL: 強力な「型」、「配列」
PostgreSQLはオブジェクトリレーショナルデータベース(ORDBMS)としての歴史が長い。 配列型(Array)を標準でサポートしている。
-- 1. テーブル作成(配列型とJSONBを使用) CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT, tags TEXT[], meta JSONB ); -- 2. データ挿入 INSERT INTO products (name, tags, meta) VALUES ('Laptop', ARRAY['tech', 'work'], '{"brand": "Apple", "os": "macOS"}'); -- 3. 配列での検索(Postgres特有) SELECT * FROM products WHERE 'tech' = ANY(tags);
MySQL: JSON関数による柔軟な対応
MySQLには「配列型」は存在しない。 JSON型と豊富なJSON関数を活して表現する。
-- 1. テーブル作成(MySQLは配列がないのでJSONで代用) CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), tags JSON, meta JSON ); -- 2. データ挿入 INSERT INTO products (name, tags, meta) VALUES ('Laptop', '["tech", "work"]', '{"brand": "Apple", "os": "macOS"}'); -- 3. JSON配列での検索 SELECT * FROM products WHERE JSON_CONTAINS(tags, '"tech"');
比較検討
| 検討項目 | MySQL | PostgreSQL |
|---|---|---|
| 得意な用途 | Webアプリ、リードヘビー(読み取り多) | 複雑な分析、データ整合性重視、地理情報 |
| データ型 | 標準的。JSON型はあるが配列型はない | 非常に豊富。配列、列挙型(ENUM)、範囲型など |
| 拡張性 | シンプルでスケールアウトしやすい | 拡張機能(PostGIS等)が強力 |
| 更新処理 | 行レベルロック。高速。 | MVCCによる多版管理。VACUUMの理解が必要 |
運用上の注意
大文字・小文字の扱い
- PostgreSQL: 識別子(テーブル名など)をダブルクォートで囲まない限りすべて小文字としてあつかわれる。
- ex:
SELECT * FROM Productsはproductsテーブルを探しに行く
- ex:
- MySQL: デフォルトではテーブル名の大文字・小文字を区別しない設定が多い
ストレージエンジンの違い
- PostgreSQL: 単一のストレージエンジン。VACUUM*(不要領域の回収)という特有の運用管理が必要
- MySQL: InnoDBが主流。用途に応じてエンジンを切り替える。
SQL標準への準拠
- PostgreSQL: SQL標準に非常に忠実。
- MySQL: 独自の便利な記法が多い傾向
まとめ
- PostgreSQLを選ぶべきケース:
- 配列型やJSONB*など、複雑なデータ構造をDB側で厳格に管理したい場合
- GIS:Geographic Information System (位置情報)データを取り扱ったり、高度な分析関数を多用する場合
- MySQLを選ぶべきケース:
- 開発コミュニティの知見を重視して、Webサイトや一般的なCMS: Contents Management Systemを構築する場合
- 読み取り性能を極限まで引き出し、シンプルなスケーリングを行いたい場合
参考
ボリュームマウントとは
コンテナを消してもデータを残すための仕組み
どうして必要?
Dockerの性質
- コンテナ削除 -> コンテナ内のデータも消える
- 再起動 -> 状態が初期化される
困ること
下記の要素は永続的に同じ状態であってほしい - DBデータ - ログ - アップロードファイル - 開発中のソースコード
仕組みのイメージ
ボリュームマウントの種類
2種類存在する
ボリューム( Docker Volume)
flowchart LR
subgraph Host["ホストOS"]
V["Docker Volume<br/>(db-data)"]
end
subgraph Docker["Docker Engine"]
C["コンテナ<br/>(MySQL)"]
end
V <-- マウント --> C
C -->|書き込み| D["/var/lib/mysql"]
特徴
- Dockerが管理する領域
- pathを意識しなくて良い
- 本番向き・安全
利用方法
docker volume create my-volume docker run \ -v my-volume:/var/lib/mysql \ mysql
コンテナを消してもmy-volumeにデータが残る
バインドマウント(Bind Mount)
flowchart LR
subgraph Host["ホストOS"]
H["./src<br/>(ローカルディレクトリ)"]
end
subgraph Docker["Docker Engine"]
C["コンテナ<br/>(Node.js)"]
A["/app/src"]
end
H <-- リアルタイム同期 --> A
A --> C
特徴
- ホストの実pathを直接利用する
- 開発向き
- path依存・事故に繋がりやすい
利用方法
docker run \ -v ./src:/app/src \ node
ローカルで対象のデータを編集すれば即時コンテナ内に反映される
違いのまとめ
| 項目 | Volume | Bind Mount |
|---|---|---|
| 管理 | Docker | 人間 |
| パス | 抽象化 | 物理パス |
| 本番 | ◎ | △ |
| 開発 | △ | ◎ |
| 安全性 | 高 | 低 |
docker-composeでの例
DB(Volume)
services: db: image: mysql volumes: - db-data:/var/lib/mysql volumes: db-data:
アプリ(Bind Mount)
services: app: image: node volumes: - ./src:/app/src
どう使い分ける?
- Volumeで使うもの
- DBデータ
- 永続化したいデータ
- 本番データ
- Bind Mountで使うもの
- 開発中のソースコード
- 設定ファイル(確認しながら編集)
- ローカル検証用
Dockerネットワークの種類
docker networkとは
コンテナ間通信のルールと通り道を作る・見る・消すコマンド
どうして必要なのか
Dockerにて下記を実現するため
- 名前解決(DNS)で接続
- 通信範囲の制御
- 各環境(ローカル, 本番)で同じ構成を再現
基本コマンド
一覧表示
docker network ls
詳細確認
docker network inspect <network名>
などが確認できる
作成
docker network create <作成したいnetwork名>
ex:
docker run --name app --network my-network app-image docker run --name db --network my-network mysql
上記ではおなじmy-networkでappとdbを繋ぐケース
IP不要で、コンテナ名=ホスト名として解決してくれる
接続/切断
docker network connect my-network container-name docker network disconnect my-network container-name
削除
docker network rm <削除したいnetwork名>
Dockerネットワークの種類
bridge
- 同一ホスト内のコンテナ通信
- Dockerが自動DNSを提供
- 基本的にこれが用いられる
ex:
docker network create my-bridge
host
- コンテナがホストOSと同じネットワーク
localhostが共有される
docker run --network host nginx
none
- ネットワーク完全遮断
overlay
- 複数ホスト間のコンテナ通信
- Kubernetesの基礎概念にちかい
docker-composeとの関係
services: app: image: app db: image: mysql
自動で専用のbridgeネットワークが作られる(ホストを一緒にして疎通できるようにしてくれる)
- app -> dbで通信可能
- 内部ではdocker networkが利用されている
IntelliJの「Load Script Configurations」と「Sync Gradle Changes」の違いを整理
IntelliJ IDEAにて build.gradle(.kts)を編集すると次の2つが表示される
- Load Script Configurations
- Sync Gradle Changes
本記事では意味がつけめるように整理
結論
| 項目 | Load Script Configurations | Sync Gradle Changes |
|---|---|---|
| 何するもの? | Gradleスクリプトの補完用設定だけ更新 | プロジェクト全体をGradleと再同期 |
| 反映される範囲 | build.gradle(.kts)のDSLモデル*のみ |
依存関係・モジュール構造・gradleタスクなど全て |
| 実行速度 | はやい | おそい(プロジェクト全体をフル同期するため) |
| 利用場面 | 補完が効かない時 | 依存追加・設定変更時 |
*DSLモデル: Domain Specific Languageの略。 Intelijがbuild.gradle.ktsを理解するための型情報・構文情報のセット
Load Script Configurationsとは
役割
Gradle スクリプト(Kotlin DSL)の補完を最新化する動作
実行場面:
依存関係やプロジェクト構造は更新されない
Sync Gradle Changesとは
役割
Gradleプロジェクト全体をIntelliJと同期するフル更新
利用場面:
- 新しい依存(implementation)を追加
- モジュール構造を変更
- buildSrcやsettings.gradleを修正
依存関係、ソースセット、タスク構成などIDEの内部モデルが再構築される
実務での使い分け
- 補完がおかしい/DSLが認識されない
- Load Script Configurations
- 依存を追加/プロジェクト構造の変更
- Sync Gradle Changes
- buildSrcの変更
- Sync Gradle Changes
まとめ
- Load Script Configurations
- スクリプトの補完更新
- Sync Gradle Changes
- プロジェクト全体の再同期
libs.version.tomlってなに?
libs.versions.tomlとは
- Gradleプロジェクトにおける外部ライブラリの依存関係の仕組み(Version Catalog)を定義するファイル
- 依存ライブラリ・plugin・versionを名前付きで管理できる
- 見てわかりやすい
libs.spring.bootのように型安全に参照できる
以下では、もうちょっと詳しく書く
利用方法
公式で提示されてる構成
webとdomainの2つのマルチプロジェクト構成
<root-project>
├── settings.gradle(.kts)
├── gradle/
│ └── libs.versions.toml
├── build.gradle(.kts)
├── web/
│ └── build.gradle(.kts)
└── domain/
└── build.gradle(.kts)
どうしてgradle/配下なの?
Gradleが自動で探索する特別なディレクトリ
- gradle/libs.versions.tomlは暗黙的に読み込まれる
- settings.gradle.ktsに何も書かなくても利用可能
- マルチプロジェクト全体で共有される
*settings.gradle.kts: Gradleプロジェクトで利用される設定ファイル。依存解決の参照先(build.gradle.ktsのrepositories)、利用する依存(build.gradle.ktsのdependencies)などbuildに関わる設定を記載するフィル
*build: 書かれたソースコードをコンピューターが理解できる形にコンパイルし、必要なライブラリやリソースをまとめてパッケージ化する工程
*マルチプロジェクト: (前提として、java, gredleで考える) 1つのルートプロジェクトの下に複数のサブプロジェクト(モジュール)をまとめて管理する仕組みのこと
この構成でできること
前提(この時の libs.versions.toml例)
# gradle/libs.versions.toml [versions] springBoot = "3.5.0" dependencyManagement = "1.1.7" junit = "5.10.2" [libraries] spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web" } spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } [plugins] spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" } dependency-management = { id = "io.spring.dependency-management", version.ref = "dependencyManagement" }
./(web|domein)/build.gradle.ktsの書き方 (各マルチプロジェクトのビルド設定)
Gradleが libs.versions.tomlより 型安全なアクセサを自動生成し、それが利用できる
各subproject(module)から下記のように記載可能
plugins {
alias(libs.plugins.spring.boot)
alias(libs.plugins.dependency.management)
}
dependencies {
implementation(libs.spring.boot.starter.web)
testImplementation(libs.spring.boot.starter.test)
testImplementation(libs.junit.jupiter)
}
共通設定: ./build.gradle.kts
subprojects {
repositories {
mavenCentral()
}
tasks.withType<Test> {
useJUnitPlatform()
}
}
うれしさ
① 文字列地獄からの解放
❌ 従来:
各subproject(module)にて同じ設定を記述する必要がある
implementation("org.springframework.boot:spring-boot-starter-web:3.5.0")
これだと下記の困りごとが出てくる
✅ Version Catalog:
利用時にideにて補完も聞き1箇所に同一設定がまとめることができる
implementation(libs.spring.boot.starter.web)
下記のうれしみがある - 補完あり - 1箇所変更(libs.versions.tomlだけ修正すれば全体の利用バージョンを上げれる) - 名前で意味が分かる
② plugin も「依存」として一元管理できる
plugins {
alias(libs.plugins.spring.boot)
}
上記を解決できる(ライブラリも plugin も同じVersion Catalogで管理)
③ マルチプロジェクトで「依存の統制」ができる
- domain が web を参照していないか?
- infrastructure に spring-web が混ざっていないか?
上記を解決し、 設計ルールを保ちやすい
よくある失敗
ルート直下に置く
ディレクトリ構成(一部抜粋)
<root> ├── libs.versions.toml ❌ //snip...
ダメなとこ
- gradleが自動認識できない
- gradleが認識のためにはsettings.gradleに設定が必要
- 対象リポジトリの共同開発者の間で設定の有無による差が出てきてしまう
- settings.gra
sebproject(module) 配下におく
ディレクトリ構成(一部抜粋)
//snip app/ └── gradle/libs.versions.toml ❌
ダメなとこ
- 配置先のsubproject(module)でしか利用できない
- マルチプロジェクト(繰り返し出てくる設定は共有できる)の良さをいかせていない
buildSrc*との役割分担 (混同しやすい)
| 項目 | libs.versions.toml | buildSrc |
|---|---|---|
| 役割 | 依存関係の定義 | 処理の共通化 |
| 主な用途 | version / library / plugin | custom task / convention plugin |
| 推奨 | まず使う | 必要になってから |
*buildSrc: Gradleにてビルドスクリプト(build.gradleやbuild.gradle.kts)の処理を再利用可能にするために特別なディレクトリ(またはプロジェクト)。共通処理をまとめることで可読性、保守性を高めれる仕組み
実務でおすすめの最小構成
制約
構成
root
├── settings.gradle.kts
├── gradle/
│ └── libs.versions.toml
├── build.gradle.kts # 共通設定のみ
├── web/
│ └── build.gradle.kts
├── domain/
│ └── build.gradle.kts
└── infrastructure/
└── build.gradle.kts
参考文献
XDG Base Directory Specification
XDG Base Directory Specificationって何
デスクトップ環境における標準的なフォルダー構造を規定した仕様
目的
煩雑になるデータを、標準的な仕様を設けることによって一貫性のある方法を提供し、整理する。
XDG Base DIrectory Specificationが規定するフォルダー
#### $XDG_DATA_HOME アプリのデータを置くフォルダーのパス.