https://www.postgresql.org/docs/current/
https://www.postgresql.org/docs/current/sql-createdatabase.html
https://www.postgresql.org/docs/current/manage-ag-tablespaces.html
https://www.postgresql.org/docs/current/user-manag.html
https://www.postgresql.org/docs/current/ddl-schemas.html
https://www.postgresql.org/docs/current/sql-createrole.html
https://www.postgresql.org/docs/current/auth-pg-hba-conf.html
https://www.postgresql.org/docs/current/runtime-config.html
https://www.postgresql.org/docs/current/monitoring-stats.html
https://www.postgresql.org/docs/current/backup.html
https://www.postgresql.org/docs/current/high-availability.html

1. Important Points#

PostgreSQL 是 relational database:
    strong SQL
    ACID transaction
    MVCC
    index / constraint / foreign key
    JSONB / full-text search / extension
    logical replication / physical streaming replication

适合:
    OLTP system
    order / payment / user / inventory
    relational data with strong consistency
    reporting query on moderate data size
    application that needs SQL correctness

不适合:
    unbounded analytical scan on massive data without warehouse design
    extremely high write fan-out without partition / queue / sharding plan
    schema-less dumping ground
    using one primary as unlimited scale point
核心原则:
    design schema and indexes with access pattern
    transaction boundary should match business invariant
    avoid long-running transactions
    every production DB needs backup restore test
    monitor connection count / lock / replication lag / bloat
    use least privilege roles
    migration must be reviewed and reversible when possible

2. Service Configuration#

database properties#

Property PostgreSQL Concept Notes
Database name CREATE DATABASE name database identifier
Owner OWNER role_name owns database and can manage database-level privileges
DBA operational/admin role label often maps to owner/admin role in tools, not a separate PostgreSQL object type
Encoding ENCODING usually UTF8
Locale provider LOCALE_PROVIDER libc / icu depending on version and build
Collation / ctype LC_COLLATE, LC_CTYPE affects sort and character classification
Tablespace TABLESPACE tablespace_name default location for database objects
Connection limit CONNECTION LIMIT per-database connection cap
Template TEMPLATE template_name source database copied when creating new DB
DBA 是什么:
    PostgreSQL 原生 CREATE DATABASE property 里没有叫 DBA 的字段。
    官方概念是 owner / role / privilege。

    很多管理平台或云数据库控制台会显示 DBA:
        usually means database administrator
        sometimes means the business owner of this database
        sometimes maps to PostgreSQL database owner role
        sometimes maps to an ops team / account outside PostgreSQL

    实战理解:
        owner:
            PostgreSQL 内部的 owning role
            can alter/drop database if it has required permissions

        DBA:
            人或团队的职责标签
            responsible for backup, access review, migration review, performance, incident
            should not be confused with application runtime user

    推荐:
        database owner role:
            dba_order

        migration role:
            order_migrator

        application role:
            order_app

        read-only role:
            order_readonly
tablespace 是什么:
    tablespace 是 PostgreSQL 里的 storage location abstraction。
    它把 database object 的 physical files 放到指定 filesystem directory。

    常见用途:
        put large index on faster disk
        separate hot table / cold archive data
        isolate IO for a specific workload
        manage storage layout on self-managed PostgreSQL

    注意:
        tablespace 不是 schema
        tablespace 不是 database
        tablespace 不是 backup boundary
        moving objects to another tablespace still needs operation planning
        managed cloud database may restrict custom tablespace usage

    默认:
        如果 CREATE DATABASE 不指定 TABLESPACE,
        database uses template database's tablespace,
        usually pg_default.

create database#

CREATE ROLE dba_order LOGIN;
CREATE ROLE order_migrator LOGIN;
CREATE ROLE order_app LOGIN;
CREATE ROLE order_readonly LOGIN;

CREATE DATABASE orders
  OWNER dba_order
  TEMPLATE template0
  ENCODING 'UTF8'
  LC_COLLATE 'C'
  LC_CTYPE 'C'
  TABLESPACE pg_default
  CONNECTION LIMIT 200;
production notes:
    owner should not be application runtime user
    app role should get only needed privileges
    connection limit protects the cluster from one database consuming all connections
    locale/collation should be chosen before production data is loaded

postgresql.conf baseline#

listen_addresses = '10.0.1.10'
port = 5432

max_connections = 200
shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 16MB
maintenance_work_mem = 1GB

wal_level = replica
max_wal_size = 8GB
min_wal_size = 1GB
checkpoint_timeout = 15min

log_min_duration_statement = 500ms
log_checkpoints = on
log_lock_waits = on
log_connections = on
log_disconnections = on
config notes:
    do not copy memory values blindly
    shared_buffers / work_mem depend on RAM and concurrency
    work_mem is per operation, not global
    log slow query before guessing indexes
    max_connections should be controlled with pooler when app count grows

pg_hba.conf baseline#

# TYPE  DATABASE  USER       ADDRESS        METHOD
local   all       postgres                  peer
hostssl orders    order_app  10.0.0.0/16    scram-sha-256
hostssl orders    dba_order  10.0.10.0/24   scram-sha-256
auth rules:
    prefer scram-sha-256
    require TLS for remote production connections
    keep CIDR narrow
    avoid trust in production

3. Data Modeling / Core Concepts#

schema#

database:
    top-level logical database
    connection targets one database

schema:
    namespace inside database
    groups tables / views / functions
    common examples:
        public
        app
        audit
        reporting

table:
    rows and columns

tablespace:
    storage location for physical files
CREATE SCHEMA app AUTHORIZATION dba_order;
CREATE SCHEMA audit AUTHORIZATION dba_order;
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE app.orders (
  id uuid PRIMARY KEY,
  user_id text NOT NULL,
  status text NOT NULL,
  amount_cents integer NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

constraints#

ALTER TABLE app.orders
  ADD CONSTRAINT orders_status_check
  CHECK (status IN ('PENDING', 'PAID', 'CANCELLED'));

ALTER TABLE app.orders
  ADD CONSTRAINT orders_amount_positive
  CHECK (amount_cents > 0);
constraint principles:
    database should enforce invariants
    application validation is not enough
    use NOT NULL / CHECK / UNIQUE / FK where they represent real business rules

indexes#

CREATE INDEX orders_user_created_idx
  ON app.orders (user_id, created_at DESC);

CREATE INDEX orders_status_created_idx
  ON app.orders (status, created_at DESC);
index principles:
    index supports query shape
    compound index order matters
    index speeds reads but slows writes
    unused indexes cost storage and write IO
    review EXPLAIN before adding random indexes

4. Query / Write Best Practices#

transaction#

BEGIN;

UPDATE app.orders
SET status = 'PAID', updated_at = now()
WHERE id = '00000000-0000-0000-0000-000000000001'
  AND status = 'PENDING';

INSERT INTO audit.order_events(order_id, event_type, created_at)
VALUES ('00000000-0000-0000-0000-000000000001', 'ORDER_PAID', now());

COMMIT;
transaction rules:
    keep transaction short
    do not wait for external API inside transaction
    update rows in consistent order to reduce deadlock
    handle serialization / deadlock retry in application

explain#

EXPLAIN (ANALYZE, BUFFERS)
SELECT *
FROM app.orders
WHERE user_id = 'user_1'
ORDER BY created_at DESC
LIMIT 20;
read plan:
    actual time
    rows estimated vs actual
    buffers hit/read
    index scan vs seq scan
    sort / hash aggregate memory

connection pool#

connection principles:
    app should use pool
    too many connections hurt memory and scheduling
    use PgBouncer when many app instances connect to one DB
    set statement_timeout / idle_in_transaction_session_timeout
ALTER ROLE order_app SET statement_timeout = '30s';
ALTER ROLE order_app SET idle_in_transaction_session_timeout = '60s';

5. Security Best Practices#

roles:
    postgres:
        emergency superuser only

    dba_order:
        database owner / DBA role

    order_migrator:
        migration role
        owns schema changes

    order_app:
        runtime role
        least privilege DML

    order_readonly:
        read-only reporting / support
REVOKE ALL ON DATABASE orders FROM PUBLIC;
GRANT CONNECT ON DATABASE orders TO order_app, order_readonly;

GRANT USAGE ON SCHEMA app TO order_app, order_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO order_app;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO order_readonly;
security checklist:
    no app superuser
    no shared DBA password in app config
    TLS enabled for remote connections
    pg_hba.conf reviewed
    default PUBLIC privileges reviewed
    secrets in secret manager
    audit privileged access

6. Backup / Restore / Reliability#

backup types:
    logical backup:
        pg_dump / pg_restore
        good for smaller DB / object-level migration

    physical backup:
        base backup + WAL archive
        required for point-in-time recovery

    managed cloud snapshot:
        convenient but restore test still required
pg_dump \
  --format=custom \
  --file=orders.dump \
  --dbname=postgresql://dba_order@db.example.com/orders

pg_restore \
  --clean \
  --if-exists \
  --dbname=postgresql://dba_order@restore.example.com/orders \
  orders.dump
reliability checklist:
    backup schedule defined
    WAL archive enabled if PITR is required
    restore test automated
    replica lag monitored
    failover runbook documented
    maintenance window defined

7. Monitoring#

-- active sessions
SELECT pid, usename, datname, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE datname = 'orders';

-- database stats
SELECT datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit
FROM pg_stat_database
WHERE datname = 'orders';

-- table bloat signals
SELECT schemaname, relname, n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
metrics:
    connections / active sessions
    TPS / commit / rollback
    cache hit ratio
    slow query count
    lock wait
    dead tuples / autovacuum
    replication lag
    disk usage / disk latency
    WAL generation rate

alerts:
    connection usage > 80%
    replication lag high
    disk free low
    long transaction age high
    lock wait spike
    autovacuum not keeping up
    backup failed

8. Hands-on#

docker#

docker run --rm --name pg-dev \
  -e POSTGRES_DB=orders \
  -e POSTGRES_USER=dba_order \
  -e POSTGRES_PASSWORD=dba_password \
  -p 5432:5432 \
  postgres:16

connect#

psql "postgresql://dba_order:dba_password@localhost:5432/orders"

inspect database properties#

SELECT
  d.datname AS database_name,
  pg_catalog.pg_get_userbyid(d.datdba) AS owner,
  d.datconnlimit AS connection_limit,
  t.spcname AS tablespace,
  d.datcollate AS collate,
  d.datctype AS ctype
FROM pg_database d
JOIN pg_tablespace t ON t.oid = d.dattablespace
WHERE d.datname = current_database();
interpretation:
    owner:
        PostgreSQL internal database owner role

    tablespace:
        default storage location for objects created in this database

    dba:
        if your UI shows DBA, map it back to owner/admin responsibility

create app schema#

CREATE SCHEMA app AUTHORIZATION dba_order;
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE app.orders (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id text NOT NULL,
  status text NOT NULL CHECK (status IN ('PENDING', 'PAID', 'CANCELLED')),
  amount_cents integer NOT NULL CHECK (amount_cents > 0),
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX orders_user_created_idx
  ON app.orders (user_id, created_at DESC);

9. Production Checklist#

database properties:
    owner / DBA responsibility documented
    application user is not owner
    tablespace decision documented
    encoding / collation chosen before production
    connection limit set

schema:
    schema names clear
    constraints enforce invariants
    indexes match query patterns
    migrations reviewed

security:
    least privilege roles
    TLS enabled
    pg_hba.conf reviewed
    PUBLIC privileges revoked where needed
    secrets managed outside git

reliability:
    backups enabled
    restore tested
    replication lag monitored
    failover runbook exists

operations:
    slow query logging enabled
    pg_stat_activity dashboard exists
    lock wait / long transaction alert exists
    autovacuum health monitored