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テーブルを探しに行く
  • MySQL: デフォルトではテーブル名の大文字・小文字を区別しない設定が多い

ストレージエンジンの違い

  • PostgreSQL: 単一のストレージエンジン。VACUUM*(不要領域の回収)という特有の運用管理が必要
  • MySQL: InnoDBが主流。用途に応じてエンジンを切り替える。

SQL標準への準拠

  • PostgreSQL: SQL標準に非常に忠実。
  • MySQL: 独自の便利な記法が多い傾向

まとめ

  • PostgreSQLを選ぶべきケース:
    • 配列型やJSONB*など、複雑なデータ構造をDB側で厳格に管理したい場合
    • GIS:Geographic Information System (位置情報)データを取り扱ったり、高度な分析関数を多用する場合
  • MySQLを選ぶべきケース:
    • 開発コミュニティの知見を重視して、Webサイトや一般的なCMS: Contents Management Systemを構築する場合
    • 読み取り性能を極限まで引き出し、シンプルなスケーリングを行いたい場合

参考

hub.docker.com

hub.docker.com

ボリュームマウントとは

コンテナを消してもデータを残すための仕組み

どうして必要?

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)の補完を最新化する動作

実行場面:

  • build.gradle.ktsの補完が効かない
  • 赤波線が消えない
  • pluginsやDSLの定義をIDEがまだ認識していない

依存関係やプロジェクト構造は更新されない

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")

これだと下記の困りごとが出てくる

  • typo で壊れる
  • バージョン更新が散らばる
  • IDE補完が弱い(ただの文字列だから)
✅ 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)の処理を再利用可能にするために特別なディレクトリ(またはプロジェクト)。共通処理をまとめることで可読性、保守性を高めれる仕組み

実務でおすすめの最小構成

制約

  • jdk 21
  • spring boot
  • Gradle kotlin dsl
  • マルチプロジェクト

構成

root
├── settings.gradle.kts
├── gradle/
│   └── libs.versions.toml
├── build.gradle.kts          # 共通設定のみ
├── web/
│   └── build.gradle.kts
├── domain/
│   └── build.gradle.kts
└── infrastructure/
    └── build.gradle.kts

参考文献

docs.gradle.org

docs.gradle.org

Linuxのコマンド入出力

Linuxのコマンド入出力

linuxのコマンドには1つの入口と2つの出口が存在する

入口

標準入力

コマンドラインに文字を入力すること

出口

コマンドラインに表示される文字

標準出力と標準エラー出力の違い

ex: rmコマンドでオプションなしでaディレクトリを消そうとした時

rm: cannot remove ‘a’: Is a directory

XDG Base Directory Specification

XDG Base Directory Specificationって何

デスクトップ環境における標準的なフォルダー構造を規定した仕様

XDG Base Directory - ArchWiki

kledgeb.blogspot.com

目的

煩雑になるデータを、標準的な仕様を設けることによって一貫性のある方法を提供し、整理する。

XDG Base DIrectory Specificationが規定するフォルダー

#### $XDG_DATA_HOME アプリのデータを置くフォルダーのパス.