5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MySQL経験者向け:PostgreSQLのschemaとroleをわかりやすく整理する

5
Posted at

MySQLに慣れている人がPostgreSQLを触り始めたとき、最初につまずきやすい概念が schemarole です。

MySQLでは、だいたい次のように考えることが多いです。

MySQL
└── database
    └── table

一方、PostgreSQLでは次のような階層で考えます。

PostgreSQL
└── database
    └── schema
        ├── table
        ├── view
        ├── function
        ├── sequence
        └── type

PostgreSQLのschemaは、単なるテーブル置き場ではありません。
そしてroleは、単なるユーザーではありません。

この記事では、MySQL経験者にもわかりやすいように、PostgreSQLの schemarole を実務目線で整理します。


PostgreSQLのschemaとは

PostgreSQLのschemaは、ひとことで言うと データベース内の名前空間 です。

かなりざっくり言えば、DBの中にある「フォルダ」のようなものです。

app_db
├── public
│   └── users
├── auth
│   └── users
└── billing
    └── invoices

このように、同じ users という名前のテーブルでも、schemaが違えば共存できます。

SELECT * FROM public.users;
SELECT * FROM auth.users;

つまり、PostgreSQLではテーブルを正確に指定するときに、

schema_name.table_name

という形で指定します。


MySQLのdatabaseとPostgreSQLのschemaは同じ?

似ていますが、完全には同じではありません。

MySQLでは、CREATE DATABASECREATE SCHEMA はほぼ同じ意味で使われます。

一方、PostgreSQLでは databaseschema は明確に別物です。

PostgreSQL
└── database
    ├── schema
    │   └── table
    └── schema
        └── table

MySQL感覚で言うと、MySQLで複数databaseに分けていた一部の用途を、PostgreSQLでは1つのdatabase内のschemaで分けることがあります。

例:

MySQLの場合

app_db
app_log_db
app_analytics_db

PostgreSQLでは、次のようにすることがあります。

PostgreSQLの場合

app_db
├── app
├── audit
└── analytics

schemaの典型的な使い方

1. 機能ごとに分ける

Webアプリケーションでは、機能やドメインごとにschemaを分けることがあります。

app_db
├── app
│   ├── projects
│   └── tasks
├── auth
│   ├── users
│   ├── sessions
│   └── password_resets
├── billing
│   ├── customers
│   ├── invoices
│   └── payments
└── audit
    └── operation_logs

このようにすると、テーブル数が増えても責務が見えやすくなります。

SELECT * FROM auth.users;
SELECT * FROM billing.invoices;
SELECT * FROM audit.operation_logs;

2. 権限管理の単位として使う

schemaは整理のためだけではありません。
権限管理の単位としてもよく使われます。

例えば、

  • アプリケーションは app schema を読み書きできる
  • BIツールは analytics schema を参照だけできる
  • 一般ユーザーには audit schema を見せない

というような設計ができます。

GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;

GRANT USAGE ON SCHEMA analytics TO bi_user;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics TO bi_user;

3. マルチテナント構成で使う

SaaSなどでは、顧客ごとにschemaを分ける設計もあります。

app_db
├── tenant_a
│   ├── users
│   └── orders
├── tenant_b
│   ├── users
│   └── orders
└── tenant_c
    ├── users
    └── orders

この構成では、テーブル構造は同じまま、データをschema単位で分離できます。

ただし、テナント数が非常に多くなる場合は管理が複雑になります。
数千、数万テナント規模を想定する場合は、schema分離ではなく、tenant_idによる行レベル分離や、DB分離も検討したほうがよいです。


4. 一時取り込み・分析・監査ログを分ける

ETLやCSVインポートなどで、一時的な取り込み先としてschemaを分けることもあります。

app_db
├── app
├── staging
├── analytics
└── audit

例:

CREATE SCHEMA staging;

CREATE TABLE staging.import_users (
  email text,
  name text,
  imported_at timestamptz DEFAULT now()
);

本番テーブルと一時テーブルを分けられるので、運用上も安全です。


public schemaとは

PostgreSQLでdatabaseを作成すると、通常 public というschemaが最初から存在します。

特にschemaを指定せずにテーブルを作ると、多くの場合 public schema に作成されます。

CREATE TABLE users (
  id bigserial PRIMARY KEY,
  name text NOT NULL
);

これは実質的に次と同じような意味になります。

CREATE TABLE public.users (
  id bigserial PRIMARY KEY,
  name text NOT NULL
);

小規模なアプリケーションでは、すべて public に置いても問題ありません。

ただし、業務アプリケーションでテーブルが増えてくる場合は、最初から次のように分けると管理しやすくなります。

app_db
├── app
├── auth
├── audit
└── analytics

search_pathとは

PostgreSQLでは、schema名を省略してテーブルを参照できます。

SELECT * FROM users;

このとき、PostgreSQLがどのschemaから探すかを決めるのが search_path です。

確認するには次のコマンドを使います。

SHOW search_path;

例えば、次のように設定されているとします。

SET search_path TO app, public;

この状態で、

SELECT * FROM users;

を実行すると、PostgreSQLはまず app.users を探します。
見つからなければ、次に public.users を探します。

つまり、次の順番です。

1. app.users
2. public.users

便利ですが、実務では注意が必要です。
同じテーブル名が複数schemaに存在する場合、意図しないテーブルを参照してしまう可能性があります。

重要なSQLでは、schema名を明示するほうが安全です。

SELECT * FROM app.users;
SELECT * FROM auth.users;

PostgreSQLのroleとは

PostgreSQLのroleは、ひとことで言うと ユーザーとグループを統合した権限管理の単位 です。

MySQLでは、一般的にユーザーを次のように作ります。

CREATE USER 'app_user'@'%' IDENTIFIED BY 'password';

PostgreSQLでは、ユーザーもroleとして作ります。

CREATE ROLE app_user LOGIN PASSWORD 'password';

ポイントは LOGIN です。

LOGIN があるroleは、ログインできるroleです。
つまり、実質的に「ユーザー」として使えます。

CREATE ROLE app_user LOGIN PASSWORD 'password';

一方、LOGIN がないroleは、ログインできません。
これは「権限グループ」のように使います。

CREATE ROLE readonly;
CREATE ROLE readwrite;

roleはユーザーにもグループにもなる

PostgreSQLでは、roleを次の2種類に分けて考えるとわかりやすいです。

role
├── ログインできるrole
│   └── ユーザーとして使う
└── ログインできないrole
    └── 権限グループとして使う

例:

CREATE ROLE app_readonly;
CREATE ROLE app_readwrite;

CREATE ROLE app_user LOGIN PASSWORD 'password';
CREATE ROLE bi_user LOGIN PASSWORD 'password';

権限グループ用roleを、ログイン用roleに付与します。

GRANT app_readwrite TO app_user;
GRANT app_readonly TO bi_user;

この設計にすると、個別ユーザーに直接権限を付けるより管理しやすくなります。


roleと権限の実務設計

例えば、次のような構成を考えます。

schema
├── app
├── auth
└── audit

role
├── app_owner
├── app_readwrite
├── app_readonly
├── app_user
└── bi_user

役割は次のように分けます。

role 用途
app_owner テーブル所有者、マイグレーション用
app_readwrite アプリケーションの通常実行用
app_readonly BI・分析・参照用
app_user アプリケーション接続ユーザー
bi_user BIツール接続ユーザー

作成例です。

CREATE ROLE app_owner LOGIN PASSWORD 'owner_password';
CREATE ROLE app_readwrite;
CREATE ROLE app_readonly;

CREATE ROLE app_user LOGIN PASSWORD 'app_password';
CREATE ROLE bi_user LOGIN PASSWORD 'bi_password';

GRANT app_readwrite TO app_user;
GRANT app_readonly TO bi_user;

schemaを作成します。

CREATE SCHEMA app AUTHORIZATION app_owner;
CREATE SCHEMA auth AUTHORIZATION app_owner;
CREATE SCHEMA audit AUTHORIZATION app_owner;

権限を付与します。

GRANT USAGE ON SCHEMA app TO app_readwrite, app_readonly;
GRANT USAGE ON SCHEMA auth TO app_readwrite, app_readonly;
GRANT USAGE ON SCHEMA audit TO app_readwrite;

GRANT SELECT, INSERT, UPDATE, DELETE
ON ALL TABLES IN SCHEMA app
TO app_readwrite;

GRANT SELECT
ON ALL TABLES IN SCHEMA app
TO app_readonly;

今後作成されるテーブルにも自動で権限を付けたい場合は、default privilegesを設定します。

ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_readwrite;

ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT ON TABLES TO app_readonly;

schema権限とtable権限は別物

PostgreSQLでよくあるハマりどころがこれです。

GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;

これだけでは不十分な場合があります。

schema内のテーブルを参照するには、通常schemaへの USAGE 権限も必要です。

GRANT USAGE ON SCHEMA app TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;

つまり、schemaとtableにはそれぞれ権限があります。

schemaへの権限
└── そのschemaを使えるか

tableへの権限
└── SELECT / INSERT / UPDATE / DELETE できるか

この違いは重要です。


MySQLとの比較

MySQLとPostgreSQLの違いをざっくり比較すると、次のようになります。

観点 MySQL PostgreSQL
database 主な分離単位 接続先のDB
schema databaseとほぼ同義で扱われることが多い database内の名前空間
user 'user'@'host' で管理 LOGIN可能なrole
role MySQL 8.0以降で利用可能 権限管理の中心概念
権限設計 userに直接付与することも多い roleに権限を付け、user roleに継承させることが多い

PostgreSQLでは、次のような考え方が基本です。

ログイン用role
↓ 所属
権限用role
↓ 権限付与
schema / table / sequence

よく使う確認コマンド

database一覧

\l

または、

psql -l

schema一覧

\dn

詳細付き:

\dn+

table一覧

\dt

schemaを指定する場合:

\dt app.*

role一覧

\du

現在のdatabase確認

SELECT current_database();

現在のschema確認

SELECT current_schema();

search_path確認

SHOW search_path;

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

小さなアプリケーションなら、最初は public だけでも動きます。
ただし、業務アプリケーションとして長く運用するなら、最初から次の程度に分けると扱いやすいです。

app_db
├── app
├── auth
├── audit
└── analytics

roleは次のように分けます。

app_owner
app_readwrite
app_readonly
app_user
bi_user

基本方針は次の通りです。

  • アプリ接続ユーザーに直接強い権限を付けない
  • 権限用roleを作り、ログイン用roleに付与する
  • schema単位で責務を分ける
  • table権限だけでなくschemaのUSAGE権限も忘れない
  • 重要なSQLではschema名を明示する

まとめ

PostgreSQLのschemaとroleは、最初は少しわかりにくいですが、実務では非常に強力です。

schemaは、DB内のオブジェクトを整理するための名前空間です。

database
└── schema
    └── table

roleは、ユーザーとグループを統合した権限管理の単位です。

role
├── ログイン用role
└── 権限グループ用role

MySQL経験者向けにまとめると、次のように考えると理解しやすいです。

MySQL:
  database中心
  user中心

PostgreSQL:
  databaseの中にschema
  userもgroupもrole

PostgreSQLらしい設計では、schemaで責務を分け、roleで権限を分けます。

schemaで構造を整理する
roleでアクセス権限を整理する

この2つを理解すると、PostgreSQLの設計・運用・セキュリティ管理がかなり見通しやすくなります。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?