開拓馬の厩

いろいろやる

MySQL しか使ったことないアプリケーション開発者が PostgreSQL の Docker 環境を立てるまで

最近仕事で PostgreSQL を使うことになったので、忘備録としてDocker で環境構築して一通り触ったメモを、主に MySQL との差分に注目しながら書きます。

PostgreSQL の Docker image

PostgreSQL は公式の docker image が公開されています

hub.docker.com

基本的に ${Postgresのバージョン}-${baseのLinux OS} の名前で 11.14-apline とか 12.9-bullseye とかのタグがつけられています。 数字のみのタグは -bullseye と同内容(Debian 11 ベース)のようです。 基本的には好みのバージョンの -alpine を使い、何か困ったら -bullseye に変える感じにすればいいと思います。

以下のような docker-compose.yaml を準備しておきます。

version: '3.7'

services:
  db:
    image: postgres:13.4-alpine
    ports:
      - 5432:5432 # postgres はデフォルトで 5432 を使う
    environment:
      - POSTGRES_HOST=localhost
      - POSTGRES_PASSWORD=password # スーパーユーザーのパスワード
      - POSTGRES_USER=admin # スーパーユーザーの名前。デフォルトは `postgres`
      - POSTGRES_DB=test_db # この環境変数で指定した名前のデータベースを自動で作ってくれる
      # パスワード無しでのログインを可能にする。MySQL における `MYSQL_ALLOW_EMPTY_PASSWORD=yes` とほぼ同じ
      # - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - postgres-tmp:/var/lib/postgresql/data

volumes:
  postgres-tmp:

POSTGRES_PASSWORD に何かしらの値を入れるあるいは POSTGRES_HOST_AUTH_METHOD=trust を付けるのどちらかを行わないと、起動時にエラーを吐きます。

対話型クライアント psql

PostgreSQLpsql という対話型 のクライアントが用意されています。MySQL における mysql コマンドとだいたい同じです。

先述の docker-compose.yaml で image を立ち上げている場合、 docker compose exec db psql -U admin -W test_db で立ち上げられます。

docker compose exec db psql -U admin -W test_db
Password: password

psql (13.4)
Type "help" for help.

test_db=#

ここに出てきたオプションの説明です。

  • -U (または --username): ユーザー名
  • -W: パスワード要求プロンプトを表示させる
  • test_db: 使用するデータベース名*1

MySQL との違いはユーザー名のあたりぐらいでしょうか(MySQL では -u または --user)

私の環境ではなぜか POSTGRES_HOST_AUTH_METHOD=trust を指定しなくてもパスワード入力を省略してログインできたり、パスワードプロンプトに間違った文字列を入力してもログインできたりする現象が発生しました(ローカル環境で動作確認をする分には問題ないのでとりあえずそのままで)。

psql を起動できたのでとりあえず色々触ってみましょう。psql にはメタコマンドと呼ばれるものが用意されており、以下のようなものがあります。

メタコマンド 説明 MySQLで似たような挙動をするやつ
\?(またはhelp) ヘルプを表示 ? help
\l データベース一覧を表示 show databases
\du ユーザー一覧を表示 select * from mysql.user; など
\dn スキーマ一覧を表示 スキーマMySQL には存在しない
\dt テーブル一覧を表示 show tables
\d {テーブル名} 指定したテーブルの概要を表示 show columns from {テーブル名}
\c {データベース名} 指定したデータベースに接続する use {データベース名}
\q psql を終了((PostgrSQL 11 以降は exit も可)) q quit exit

とりえあず、適当にテーブルを作って、適当にデータを追加して、適当に取得してみます。

データベース一覧を見る

test_db=# \l
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges
-----------+-------+----------+------------+------------+-------------------
 postgres  | admin | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | admin | UTF8     | en_US.utf8 | en_US.utf8 | =c/admin         +
           |       |          |            |            | admin=CTc/admin
 template1 | admin | UTF8     | en_US.utf8 | en_US.utf8 | =c/admin         +
           |       |          |            |            | admin=CTc/admin
 test_db   | admin | UTF8     | en_US.utf8 | en_US.utf8 |

POSTGRES_DB で指定した test_db というデータベースが作成されています。

テーブル作成

適当に CREATE TABLE します。

test_db=# CREATE TABLE "test_table" (
  "id" VARCHAR(32) NOT NULL,
  "name" VARCHAR(255) NOT NULL,
  "tags" VARCHAR(255)[] NOT NULL,
  "created_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY ("id")
);

CREATE TABLE

\dt でテーブル一覧を表示すると、作成した test_table が確認できます。

test_db=# \dt
          List of relations
 Schema |    Name    | Type  | Owner
--------+------------+-------+-------
 public | test_table | table | admin

\d test_tabletest_table の中身を見ます。

test_db=# \d test_table;
                               Table "public.test_table"
   Column   |              Type              | Collation | Nullable |      Default
------------+--------------------------------+-----------+----------+-------------------
 id         | character varying(32)          |           | not null |
 name       | character varying(255)         |           | not null |
 tags       | character varying(255)[]       |           | not null |
 created_at | timestamp(6) without time zone |           | not null | CURRENT_TIMESTAMP
Indexes:
    "test_table_pkey" PRIMARY KEY, btree (id)

PRIMARY KEY の指定方法が MySQL とちょっと違いますね。あと、 PostgreSQL は配列型を使えるようで便利そうですね。*2

データを保存・取得

test_table にデータを適当に insert してみます。

test_db=# INSERT INTO test_table
  ("id", "name", "tags") VALUES
  ( 'id1234', 'MyName', Array['tag1','tag2']);

INSERT 0 1

配列は Array[] で囲むか {} で囲むかすると表現できるようです。あとは連結演算子 || があって Array[1,2] || 3 とか書くと Array[1,2,3] になったりとか便利そうな機能が用意されています。

データ取得も普通に SELECT 文を書くだけです。

test_db=# SELECT * FROM "test_table" WHERE "id"='id1234';

   id   |  name  |    tags     |         created_at
--------+--------+-------------+----------------------------
 id1234 | MyName | {tag1,tag2} | 2022-02-03 09:55:55.306328

アプリケーション開発の場面では、実際のデータベースへのアクセスはほとんどの場合ライブラリ任せで生のクエリを書くことはあまりないので(特に書き込み系)動作確認はこれぐらいで。

スキーマ

PostgreSQL にはデータベースとテーブルの間にスキーマという層が存在し、三層構造になっています。MySQL にはスキーマが存在せず二層であり、このあたりが両者の大きな相違点なのかなと思います。

f:id:pf_siedler:20220207183647p:plain
データベース・スキーマ・テーブルの関係
PostgreSQLにおけるデータベース、スキーマ、テーブルの関係 -- DBOnline から引用

デフォルトでは public という名前のスキーマが用意されており、クエリが省略されているときは自動で補完されます。 public スキーマのみを使用すれば MySQL とだいたい同じ使用感になると思われますが、 MySQL では test_db.test_table と書けたところが PostgreSQL では test_db.public.table になる点は注意したいですね。

おわりに(日記)

本稿では、 PostgreSQL の Docker image を構築し、一通り触ってみました。

余談ですが、ポスグレを使うことになった経緯について少し書きます。

そこそこ大きな Node.js 製アプリケーションの一部をマイクロサービス化しつつちょっと新機能を追加する試みを進めており、その一環で私は現在 TypeScript で小さめのアプリケーションを作っています。 既存アプリケーションからのデータの移行等はなく、データベースは新規に作ったものを使うことになりました。 ある程度プロトタイプの開発を進めて方向性が決まった段階で、データベースの選定について SRE に相談したところ、 PostgreSQL を使うことになりました。 弊社ではこれまで RDBMySQL を使っていましたが、 PostgreSQL の知見を貯める意味で試験的にやってみよう❗という感じです。

当初は前例に習って MySQL を使うつもりで進めていましたが途中で postgreSQL に切り替えるという状況になったのですが、データベースの操作は ORM ライブラリの Prisma*3 を使用しており、設定ファイルをちょっと書き換えるだけで、アプリケーションの実装はほぼそのままで移行が住みました。

以上、 ORM って便利ですねという日記でした。

この記事は業務時間中に書きました。 仕事の一貫なので弊社の宣伝を

↓弊社開発チームメンバーのブログ記事集です↓

tech-hub.herp.co.jp

↓我々と一緒に働きたい人を募集中です↓

careers.herp.co.jp

参考資料

*1:psql のオプション処理が悪いのか docker との相性なのか不明だが、 docker compose exec db psql -U admin とやると admin の箇所がユーザー名かつデータベース名だと判定される現象が発生する

*2:ただ、配列を濫用すると後々管理が大変になるため、別テーブルを作って relation を貼る方が好ましい場合が多い

*3:Prisma の記事もいつか書きたいです。いつかね……