PostgreSQL全栈指南:从入门到精通

🐘 PostgreSQL 完整知识总结与使用教程


📖 目录


一、PostgreSQL 概述

1.1 什么是 PostgreSQL

PostgreSQL (常简称为 Postgres )是一个功能强大的开源对象关系型数据库管理系统 (ORDBMS, Object-Relational Database Management System)。它起源于加州大学伯克利分校的 POSTGRES 项目,至今已经历了 40 多年 的持续开发与演进。

PostgreSQL 以其稳定性、可扩展性、符合 SQL 标准以及丰富的高级功能而闻名,被誉为"世界上功能最强大的开源数据库"。截至最新版本 PostgreSQL 17/18,它已对 SQL:2023 标准的 177 项强制特性中的 170 项实现了兼容,是 SQL 标准兼容性最高的数据库之一。

1.2 为什么选择 PostgreSQL

PostgreSQL

核心优势
开源免费
PostgreSQL License
类似 MIT/BSD
可商用可修改
功能强大
ACID 完全兼容
MVCC 并发
SQL 标准兼容度最高
事务嵌套
扩展性超强
自定义类型
自定义函数
自定义索引
PostGIS 地理
pgvector AI
TimescaleDB 时序
数据类型丰富
JSON / JSONB
数组
几何
范围
UUID v7
向量
生态繁荣
全球活跃社区
每年一个主版本
大厂深度参与
企业级可靠
流复制
逻辑复制
PITR
高可用

优势 说明
完全开源免费 PostgreSQL License(类似 MIT/BSD),可商用/修改/分发
高度可扩展 支持自定义类型、函数、操作符、索引
严格的 ACID 事务 核心架构原生支持,而非依赖存储引擎
卓越的并发性能 MVCC 实现,读写互不阻塞
丰富的数据类型 JSON、数组、几何、范围、向量等
强大的插件生态 PostGIS、TimescaleDB、pgvector 等
活跃的全球社区 40 余年持续演进,每年一个主版本
SQL 标准兼容性最强 SQL:2023 的 170/177 项强制特性

1.3 典型应用场景

场景 说明
OLTP 事务系统 金融、电商、ERP 等要求强一致性的系统
OLAP 分析系统 并行查询、列式扩展(如 Citus)支持分析负载
GIS 地理信息 通过 PostGIS 扩展,成为世界级开源 GIS 数据库
时序数据 通过 TimescaleDB 扩展处理 IoT、监控数据
AI / 向量搜索 通过 pgvector 支持 RAG、语义搜索、推荐系统
文档数据库 JSONB 原生支持,可替代 MongoDB 部分场景
全文搜索 内置 tsvector / tsquery 全文检索功能

二、发展历史与现状

2.1 PostgreSQL 简史

1986 Michael Stonebraker 在伯克利启动 POSTGRES 项目 1994 增加 SQL 语言解释器,更名为 Postgres95 1996 正式更名为 PostgreSQL 2005 PostgreSQL 8.0 发布,原生支持 Windows 2010 PostgreSQL 9.0 引入流复制(Streaming Replication) 2014 PostgreSQL 9.4 引入 JSONB 类型 2017 PostgreSQL 10 引入逻辑复制、声明式分区 2019 PostgreSQL 12 大幅提升分区性能 2023 PostgreSQL 16 并行性能进一步增强 2024 PostgreSQL 17 优化 VACUUM 内存管理 2025 PostgreSQL 18 引入异步 I/O (AIO) 2026 PostgreSQL 19 预计 9 月发布 PostgreSQL 发展时间线

2.2 版本策略

PostgreSQL 遵循每年一次主版本发布的节奏:

  • 主版本:包含重大新特性,每年 9-10 月发布
  • 次版本:每季度发布(2月、5月、8月、11月的第二个周四),修复 bug 和安全问题
  • 支持周期 :每个主版本支持 5 年

2.3 当前最新版本

截至 2026 年 4 月,PostgreSQL 各版本状态:

版本 状态 最新子版本 说明
PostgreSQL 18 ✅ 最新主版本 18.3 引入异步 I/O、UUIDv7
PostgreSQL 17 ✅ 稳定支持 17.9 内存管理优化
PostgreSQL 16 ✅ 稳定支持 16.13 逻辑复制增强
PostgreSQL 15 ✅ 稳定支持 15.17 MERGE 语句
PostgreSQL 14 ✅ 稳定支持 14.22 即将 EOL
PostgreSQL 19 🔜 规划中 - 预计 2026 年 9 月

三、核心特性

3.1 ACID 完全兼容

PostgreSQL 原生核心架构 完全支持 ACID:

  • A (Atomicity) --- 原子性:事务要么全做,要么全不做
  • C (Consistency) --- 一致性:事务前后数据满足约束
  • I (Isolation) --- 隔离性:并发事务互不干扰
  • D (Durability) --- 持久性:提交数据永久保存(通过 WAL)

3.2 丰富的数据类型

PostgreSQL 支持的数据类型数量是所有关系型数据库中最多的

复制代码
📊 数值类型:smallint, integer, bigint, decimal, numeric, real, double precision, serial
📝 字符类型:char, varchar, text
📅 时间类型:date, time, timestamp, timestamptz, interval
🔢 布尔类型:boolean
🎯 几何类型:point, line, lseg, box, path, polygon, circle
🌐 网络地址:cidr, inet, macaddr
📦 JSON 类型:json, jsonb
🧬 数组类型:任何类型的多维数组
🆔 UUID 类型:uuid(PostgreSQL 18 支持 uuidv7)
📊 范围类型:int4range, tstzrange 等
🔠 枚举类型:CREATE TYPE ... AS ENUM
🧠 自定义复合类型与域类型
🔍 全文搜索:tsvector, tsquery
🧪 XML 类型
💾 二进制:bytea

3.3 MVCC 多版本并发控制

PostgreSQL 采用 MVCC(Multi-Version Concurrency Control) 实现读写并发:

读不阻塞写,写不阻塞读

这是 PostgreSQL 最核心的并发特性,后文会深入解析。

3.4 扩展性(Extensibility)

PostgreSQL 最独特的优势 ------ 可扩展性

  • ✅ 自定义数据类型
  • ✅ 自定义函数(PL/pgSQL、PL/Python、PL/Perl、PL/Tcl、PL/Java 等)
  • ✅ 自定义操作符
  • ✅ 自定义聚合函数
  • ✅ 自定义索引类型
  • ✅ 外部数据包装器(FDW, Foreign Data Wrapper)
  • ✅ 扩展(Extension)系统:一行 CREATE EXTENSION 即可加载

3.5 其他高级特性

复制代码
🔄 同步、异步、流、逻辑复制
🔐 多种认证方式:md5/scram-sha-256/LDAP/GSSAPI/SSPI/Cert/OAuth
🛡️ 行级安全策略(Row-Level Security)
🔍 全文搜索(内置)
🧬 表继承(Table Inheritance)
📦 声明式分区(Declarative Partitioning)
⚡ 并行查询
💾 物化视图(Materialized View)
⏳ 时点恢复(PITR, Point-in-Time Recovery)
🧪 CTE、递归查询、窗口函数、LATERAL 连接

四、PostgreSQL 架构深入解析

4.1 整体架构图

PostgreSQL 采用 客户端/服务器(C/S)架构"进程每连接"(Process-per-connection) 模型:
💾 磁盘存储
PostgreSQL 实例
后台辅助进程
后端进程池
TCP :5432
共享内存 Shared Memory
Shared Buffers

数据页缓存
WAL Buffers

WAL缓冲区
CLOG Buffer

提交日志
Lock Space

锁空间
客户端 Clients
psql
pgAdmin
Application

JDBC / libpq / ODBC
🔹 Postmaster

主守护进程
Backend 1
Backend 2
Backend N
Background Writer

后台写进程
Checkpointer

检查点进程
WAL Writer

WAL写进程
WAL Archiver

归档进程
Autovacuum

自动清理
I/O Workers

异步I/O - PG18+
Data Files

base/*
WAL Files

pg_wal/*
配置文件

*.conf

4.2 进程结构

PostgreSQL 启动后,会派生多个进程协同工作:

🔸 Postmaster(主进程)
  • 数据库启动时第一个启动的进程
  • 监听客户端连接请求
  • 为每个连接 fork 一个 backend 进程
  • 是所有其他进程的父进程
🔸 Backend 进程(后端进程)
  • 每个客户端连接对应一个
  • 处理该连接的 SQL 语句
  • 最大数量由 max_connections 参数控制(默认 100)
  • 单线程,每次处理一条 SQL
🔸 后台辅助进程
进程名称 职责
Background Writer 定期将"脏页"(dirty page)从共享缓冲区写到磁盘
Checkpointer 执行检查点,把所有脏页、WAL 日志刷到磁盘
WAL Writer 定期将 WAL 缓冲区内容写入 WAL 日志文件
WAL Archiver 将归档 WAL 日志复制到安全位置(用于 PITR)
Autovacuum Launcher 调度自动 vacuum 清理死元组
Autovacuum Worker 执行 vacuum / analyze 操作
Logger 收集日志信息,写入日志文件
Stats Collector 收集数据库统计信息
Logical Replication Worker 逻辑复制工作进程
I/O Worker PostgreSQL 18 新增:异步 I/O 工作进程

4.3 内存结构

PostgreSQL 内存分为 共享内存本地内存 两部分:

🔸 共享内存(Shared Memory)

所有进程共享,实例启动时分配:

复制代码
┌────────────────────────────────────────────────┐
│  shared_buffers        │ 数据页缓存              │
│                        │ 建议:系统内存 25-40%   │
├────────────────────────┼────────────────────────┤
│  wal_buffers           │ WAL 预写日志缓冲区       │
│                        │ 默认:shared_buffers/32 │
├────────────────────────┼────────────────────────┤
│  CLOG buffers          │ 提交日志缓冲              │
├────────────────────────┼────────────────────────┤
│  Lock space            │ 锁信息存储                │
├────────────────────────┼────────────────────────┤
│  Other shared areas    │ 其他共享数据              │
└────────────────────────────────────────────────┘

关键参数解释

  • shared_buffers :PostgreSQL 自己管理的数据页缓存,默认 128MB,生产环境通常设为物理内存的 25%-40%
  • wal_buffers :WAL 日志写入磁盘前的缓冲区,默认自动计算为 shared_buffers / 32,最大 16MB
🔸 本地内存(Local / Per-backend Memory)

每个 backend 进程私有:

参数 默认值 用途
work_mem 4MB 排序、哈希连接、位图操作
maintenance_work_mem 64MB VACUUM、CREATE INDEX、ALTER TABLE 等维护操作
temp_buffers 8MB 临时表访问
effective_cache_size 4GB 查询优化器估算可用的总缓存(含 OS 缓存)

4.4 存储结构

🔸 数据目录 (PGDATA)

默认路径(Linux):/var/lib/postgresql/<版本>/main/

复制代码
PGDATA/
├── base/                 # 每个数据库的数据文件目录
│   └── <db_oid>/
│       └── <table_oid>   # 每张表/索引对应一个文件
├── global/               # 全局对象(pg_database 等)
├── pg_wal/               # WAL 预写日志文件(旧版 pg_xlog)
├── pg_xact/              # 事务提交状态(旧版 pg_clog)
├── pg_multixact/         # 多事务状态
├── pg_stat/              # 统计信息
├── pg_tblspc/            # 表空间符号链接
├── postgresql.conf       # 主配置文件
├── pg_hba.conf           # 客户端认证配置
└── pg_ident.conf         # 用户名映射配置
🔸 数据页(Page/Block)
  • PostgreSQL 存储以**页(Page)**为单位,默认 8KB
  • 每个表/索引被分成多个 8KB 的页
  • 一个页内包含多行数据(tuples)
  • 页首(Page Header)→ 行指针数组 → 空闲空间 → 行数据(从页尾向前填充)

4.5 数据写入流程

了解数据是如何从内存写到磁盘的,对性能调优至关重要:
Background Writer Checkpointer WAL 文件 pg_wal/* 数据文件 base/* WAL Buffers Shared Buffers Backend 进程 客户端 Background Writer Checkpointer WAL 文件 pg_wal/* 数据文件 base/* WAL Buffers Shared Buffers Backend 进程 客户端 定期异步执行... ⚡ 只要 WAL 落盘,即使崩溃也能恢复 1. 发送 UPDATE 语句 1 2. 加载数据页到缓存 2 3. 修改数据页 (产生脏页) 3 4. 生成 WAL 记录 4 5. COMMIT 5 6. WAL 落盘 ✅ 【持久化完成】 6 7. 返回成功 7 8. 读取脏页 8 9. 脏页刷入数据文件 9

关键要点

  • COMMIT只需保证 WAL 日志落盘(而不是数据文件),这就是"WAL (Write-Ahead Logging) 预写日志" 机制。
  • ✅ 即使崩溃,重启时可通过 WAL 重做(REDO)恢复数据。
  • ✅ 顺序写 WAL 比随机写数据页快 1~2 个数量级
  • ✅ 这是 PostgreSQL 高吞吐又保证持久化的核心秘密。

五、安装与部署

5.1 Linux(Ubuntu/Debian)安装

bash 复制代码
# 1. 添加 PostgreSQL 官方仓库
sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
  sudo apt-key add -

# 2. 更新并安装最新版 PostgreSQL
sudo apt update
sudo apt install -y postgresql-18 postgresql-client-18

# 3. 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql
sudo systemctl status postgresql

# 4. 切换到 postgres 用户登录
sudo -u postgres psql

5.2 Linux(CentOS/RHEL/Rocky)安装

bash 复制代码
# 1. 添加 PostgreSQL 官方仓库
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 2. 禁用内置 PostgreSQL 模块
sudo dnf -qy module disable postgresql

# 3. 安装 PostgreSQL 18
sudo dnf install -y postgresql18-server postgresql18-contrib

# 4. 初始化数据库
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb

# 5. 启动服务
sudo systemctl start postgresql-18
sudo systemctl enable postgresql-18

5.3 macOS 安装

方式一:Homebrew(推荐)

bash 复制代码
brew install postgresql@18
brew services start postgresql@18

方式二:Postgres.app

https://postgresapp.com 下载图形化安装包,双击安装即可。

5.4 Windows 安装

EDB 官方下载页 下载安装程序:

  1. 双击运行 .exe 安装包
  2. 选择安装路径(如 C:\Program Files\PostgreSQL\18
  3. 选择组件:PostgreSQL Server、pgAdmin 4、命令行工具
  4. 设置数据目录(默认 C:\Program Files\PostgreSQL\18\data
  5. 设置 postgres 超级用户密码(请牢记!)
  6. 设置监听端口(默认 5432)
  7. 完成安装,可选安装 Stack Builder 安装扩展

5.5 Docker 快速部署

最简单的方式 ------ 使用 Docker:

bash 复制代码
# 拉取官方镜像
docker pull postgres:18

# 启动容器
docker run --name mypg \
  -e POSTGRES_PASSWORD=mysecret \
  -e POSTGRES_DB=mydb \
  -e POSTGRES_USER=myuser \
  -p 5432:5432 \
  -v pg_data:/var/lib/postgresql/data \
  -d postgres:18

# 进入容器
docker exec -it mypg psql -U myuser -d mydb

5.6 关键配置文件

🔸 postgresql.conf ------ 主配置文件
conf 复制代码
# 监听地址
listen_addresses = '*'              # '*' 表示所有地址
port = 5432

# 连接与认证
max_connections = 200

# 内存配置(基于 16GB 内存服务器的建议)
shared_buffers = 4GB                # 物理内存 25%
work_mem = 16MB
maintenance_work_mem = 1GB
effective_cache_size = 12GB         # 物理内存 75%

# WAL 配置
wal_level = replica                 # minimal / replica / logical
max_wal_size = 2GB
min_wal_size = 80MB
checkpoint_timeout = 15min

# 查询优化
random_page_cost = 1.1              # SSD 推荐 1.1,HDD 推荐 4.0
effective_io_concurrency = 200      # SSD 推荐 200,HDD 推荐 2

# 日志
log_destination = 'stderr'
logging_collector = on
log_directory = 'log'
log_min_duration_statement = 1000   # 记录执行超过 1s 的慢查询
🔸 pg_hba.conf ------ 客户端认证配置

HBA = Host-Based Authentication

conf 复制代码
# TYPE    DATABASE    USER    ADDRESS         METHOD
local     all         all                     peer
host      all         all     127.0.0.1/32    scram-sha-256
host      all         all     ::1/128         scram-sha-256
host      all         all     0.0.0.0/0       scram-sha-256   # 允许远程

认证方式说明

方法 说明
trust 无条件信任(仅限开发环境)
peer 使用操作系统同名用户认证(仅限本地)
md5 MD5 加密密码(已在 PG18 弃用)
scram-sha-256 推荐,SCRAM-SHA-256 认证
cert SSL 客户端证书认证
oauth PG18 新增,OAuth 2.0 认证
ldap LDAP 认证

修改配置后重载:

bash 复制代码
sudo systemctl reload postgresql
# 或者在 psql 中执行
SELECT pg_reload_conf();

5.7 客户端工具

工具 类型 特点
psql 命令行 官方标准,功能完整
pgAdmin 4 GUI (Web) 官方图形化工具
DBeaver GUI (桌面) 跨平台,支持多种数据库
DataGrip GUI (商业) JetBrains 出品,体验极佳
Navicat GUI (商业) 功能丰富,支持中文
TablePlus GUI 轻量、美观、跨平台
DBeaver GUI 开源免费,功能强大

5.8 psql 常用命令速查

sql 复制代码
-- 连接数据库
psql -h localhost -p 5432 -U myuser -d mydb

-- 元命令(以 \ 开头)
\l                 -- 列出所有数据库
\c dbname          -- 切换到 dbname 数据库
\dt                -- 列出所有表
\d tablename       -- 查看表结构
\di                -- 列出所有索引
\dv                -- 列出所有视图
\df                -- 列出所有函数
\du                -- 列出所有用户/角色
\dn                -- 列出所有 schema
\dx                -- 列出所有已安装扩展
\timing            -- 打开/关闭计时
\e                 -- 打开编辑器
\i file.sql        -- 执行文件
\q                 -- 退出
\?                 -- 查看所有元命令
\h SELECT          -- 查看 SQL 命令帮助

六、基础 SQL 操作

6.1 数据库操作

sql 复制代码
-- 创建数据库
CREATE DATABASE mydb
  WITH OWNER = myuser
  ENCODING = 'UTF8'
  LC_COLLATE = 'en_US.UTF-8'
  LC_CTYPE = 'en_US.UTF-8'
  TEMPLATE = template0;

-- 修改数据库
ALTER DATABASE mydb RENAME TO newdb;
ALTER DATABASE mydb OWNER TO newuser;

-- 删除数据库
DROP DATABASE IF EXISTS mydb;

-- 查看所有数据库
SELECT datname FROM pg_database;

6.2 Schema 操作

PostgreSQL 的 Schema 类似命名空间,可以组织对象:

sql 复制代码
-- 创建 schema
CREATE SCHEMA sales;

-- 切换默认 schema
SET search_path TO sales, public;

-- 在指定 schema 下建表
CREATE TABLE sales.orders (...);

-- 删除 schema
DROP SCHEMA sales CASCADE;

6.3 表操作(DDL)

sql 复制代码
-- 创建表
CREATE TABLE users (
    id          SERIAL PRIMARY KEY,         -- 自增主键
    username    VARCHAR(50) UNIQUE NOT NULL,
    email       VARCHAR(100) UNIQUE,
    password    TEXT NOT NULL,
    age         INTEGER CHECK (age >= 0),
    gender      VARCHAR(10) DEFAULT 'unknown',
    profile     JSONB,
    tags        TEXT[],
    created_at  TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- 创建外键表
CREATE TABLE orders (
    id          BIGSERIAL PRIMARY KEY,
    user_id     INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    total       NUMERIC(10, 2) NOT NULL,
    status      VARCHAR(20) DEFAULT 'pending',
    created_at  TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- 添加/删除列
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;

-- 修改列类型
ALTER TABLE users ALTER COLUMN age TYPE SMALLINT;

-- 添加约束
ALTER TABLE users ADD CONSTRAINT chk_age CHECK (age < 150);

-- 重命名表
ALTER TABLE users RENAME TO app_users;

-- 清空表(保留结构)
TRUNCATE TABLE users RESTART IDENTITY CASCADE;

-- 删除表
DROP TABLE IF EXISTS users CASCADE;

6.4 数据操作(DML)

INSERT
sql 复制代码
-- 单行插入
INSERT INTO users (username, email, password)
VALUES ('alice', 'alice@example.com', 'hash');

-- 批量插入
INSERT INTO users (username, email, password) VALUES
    ('bob',    'bob@example.com',    'hash1'),
    ('carol',  'carol@example.com',  'hash2'),
    ('dave',   'dave@example.com',   'hash3');

-- 插入并返回
INSERT INTO users (username, email) 
VALUES ('eve', 'eve@example.com') 
RETURNING id, created_at;

-- UPSERT(插入或更新)
INSERT INTO users (username, email, password)
VALUES ('alice', 'alice@new.com', 'newhash')
ON CONFLICT (username) 
DO UPDATE SET 
    email = EXCLUDED.email,
    password = EXCLUDED.password,
    updated_at = NOW();

-- 从查询插入
INSERT INTO users_archive 
SELECT * FROM users WHERE created_at < '2024-01-01';
SELECT
sql 复制代码
-- 基础查询
SELECT * FROM users WHERE age > 18;

-- 条件组合
SELECT * FROM users 
WHERE (age BETWEEN 20 AND 30) 
  AND (email LIKE '%@example.com')
  AND (gender IN ('male', 'female'))
ORDER BY created_at DESC 
LIMIT 10 OFFSET 20;

-- 聚合查询
SELECT 
    gender,
    COUNT(*) AS total,
    AVG(age) AS avg_age,
    MIN(age) AS min_age,
    MAX(age) AS max_age
FROM users
GROUP BY gender
HAVING COUNT(*) > 10
ORDER BY total DESC;

-- 多表连接
SELECT u.username, o.total, o.created_at
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 'paid';

-- LEFT/RIGHT/FULL JOIN
SELECT u.username, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;

-- 子查询
SELECT * FROM users 
WHERE id IN (SELECT user_id FROM orders WHERE total > 1000);

-- EXISTS 子查询
SELECT * FROM users u
WHERE EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.user_id = u.id AND o.status = 'paid'
);

-- DISTINCT
SELECT DISTINCT gender FROM users;
SELECT DISTINCT ON (user_id) * FROM orders ORDER BY user_id, created_at DESC;
UPDATE
sql 复制代码
-- 基础更新
UPDATE users 
SET age = age + 1, updated_at = NOW()
WHERE id = 1;

-- 条件更新
UPDATE users 
SET status = 'inactive' 
WHERE last_login < NOW() - INTERVAL '1 year';

-- 从其他表更新
UPDATE orders o
SET user_name = u.username
FROM users u
WHERE o.user_id = u.id;

-- 更新并返回
UPDATE users 
SET password = 'newhash' 
WHERE username = 'alice'
RETURNING id, email, updated_at;
DELETE
sql 复制代码
-- 条件删除
DELETE FROM users WHERE age < 18;

-- 关联删除
DELETE FROM orders 
WHERE user_id IN (SELECT id FROM users WHERE status = 'deleted');

-- 删除并返回
DELETE FROM users WHERE id = 100 RETURNING *;

-- TRUNCATE(更快,但不触发触发器,不可回滚某些操作)
TRUNCATE TABLE logs;

6.5 MERGE 语句(PG 15+)

PostgreSQL 15 引入的 SQL 标准 MERGE:

sql 复制代码
MERGE INTO inventory AS i
USING (SELECT product_id, quantity FROM purchases) AS p
  ON i.product_id = p.product_id
WHEN MATCHED THEN
    UPDATE SET stock = i.stock + p.quantity
WHEN NOT MATCHED THEN
    INSERT (product_id, stock) VALUES (p.product_id, p.quantity);

七、数据类型全解

7.1 数值类型

类型 存储 范围 说明
SMALLINT 2 字节 -32,768 ~ +32,767 小整数
INTEGER / INT 4 字节 -21 亿 ~ +21 亿 常用整数
BIGINT 8 字节 约 ±9.2×10¹⁸ 大整数
DECIMAL / NUMERIC(p,s) 变长 最高 131,072 位整数 精确小数
REAL 4 字节 单精度浮点 约 6 位十进制精度
DOUBLE PRECISION 8 字节 双精度浮点 约 15 位十进制精度
SERIAL 4 字节 自增整数 等同 INT + AUTO_INCREMENT
BIGSERIAL 8 字节 自增大整数 大表主键推荐
MONEY 8 字节 货币 不推荐,使用 NUMERIC 替代

💡 实践建议 :涉及金额必须使用 NUMERIC,绝不要用 FLOATREAL,因为浮点运算会有精度丢失!

7.2 字符类型

类型 说明
CHAR(n) 定长,不足补空格(不推荐)
VARCHAR(n) 变长,最大 n 字符
TEXT 变长,无长度限制(推荐)

💡 PostgreSQL 中 TEXTVARCHARVARCHAR(n) 性能差异几乎可忽略 ,推荐使用 TEXT 或不带长度的 VARCHAR

7.3 日期时间类型

类型 格式示例 存储 说明
DATE 2026-04-17 4 字节 仅日期
TIME 14:30:00 8 字节 仅时间
TIMESTAMP 2026-04-17 14:30:00 8 字节 无时区
TIMESTAMPTZ 2026-04-17 14:30:00+08 8 字节 带时区(推荐)
INTERVAL 1 day 2 hours 16 字节 时间间隔
sql 复制代码
-- 日期函数示例
SELECT CURRENT_DATE;                          -- 2026-04-17
SELECT CURRENT_TIME;                          -- 14:30:00
SELECT CURRENT_TIMESTAMP;                     -- 2026-04-17 14:30:00+08
SELECT NOW();                                 -- 同 CURRENT_TIMESTAMP
SELECT DATE_TRUNC('month', NOW());            -- 2026-04-01
SELECT EXTRACT(YEAR FROM NOW());              -- 2026
SELECT AGE(TIMESTAMP '1990-01-01');           -- 年龄
SELECT NOW() + INTERVAL '1 day 3 hours';      -- 时间加法
SELECT NOW() - INTERVAL '1 week';             -- 时间减法

7.4 布尔、UUID 与其他类型

sql 复制代码
-- 布尔
CREATE TABLE t (
    is_active BOOLEAN DEFAULT TRUE,
    is_admin  BOOL NOT NULL   -- BOOL 是 BOOLEAN 的别名
);
-- TRUE / FALSE / NULL
-- 输入别名: 't', 'true', 'yes', 'y', '1' / 'f', 'false', 'no', 'n', '0'

-- UUID(PG18 原生支持 UUIDv7)
CREATE TABLE t (
    id UUID DEFAULT gen_random_uuid()          -- UUIDv4
    -- id UUID DEFAULT uuidv7()                -- UUIDv7(PG18+)
);

-- ENUM 枚举类型
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'delivered');
CREATE TABLE orders (
    status order_status DEFAULT 'pending'
);

-- 数组类型
CREATE TABLE posts (
    tags TEXT[],
    ratings INTEGER[][]
);
INSERT INTO posts VALUES ('{"postgres","sql","tutorial"}', '{{5,4},{3,2}}');
SELECT * FROM posts WHERE 'postgres' = ANY(tags);
SELECT * FROM posts WHERE tags @> ARRAY['sql'];   -- 包含

-- 范围类型
CREATE TABLE reservations (
    room_id INTEGER,
    period  TSTZRANGE
);
INSERT INTO reservations VALUES 
    (1, '[2026-04-17 14:00, 2026-04-17 16:00)');
SELECT * FROM reservations 
WHERE period && '[2026-04-17 15:00, 2026-04-17 17:00)'::tstzrange;  -- 重叠

-- 几何类型(内置)
SELECT POINT(1.5, 2.5);
SELECT BOX '((0,0),(1,1))';
SELECT CIRCLE '<(0,0),5>';

-- 网络类型
CREATE TABLE servers (
    ip  INET,
    subnet CIDR,
    mac MACADDR
);
INSERT INTO servers VALUES ('192.168.1.1', '192.168.1.0/24', '08:00:2b:01:02:03');

八、索引技术详解

索引是提升查询性能的最重要手段 。PostgreSQL 提供了多种索引类型,各有适用场景。

8.1 索引类型总览

索引类型 适用场景 默认 典型用途
B-tree 等值、范围、排序 默认索引,90% 场景
Hash 等值查询 = 精确匹配,PG10+ 支持 WAL
GIN 多值列(数组、JSONB、全文) 倒排索引
GiST 几何、全文、范围 通用搜索树,支持 KNN
SP-GiST 非平衡数据(四叉树、k-d树) IP 路由、电话路由
BRIN 大表上顺序相关的列 时序数据、日志表
Bloom 多列等值过滤 扩展 仓储数据、多条件查询

索引选型决策树
标量:数字/字符串/日期
数组、JSONB、全文
几何、地理数据
IP、电话、非均衡树
大表 + 顺序写入

如日志/时序
等值 + 范围 + 排序
仅等值查询

否,纯=
需要建立索引
数据类型是什么?
查询模式?
🎯 GIN

倒排索引
🎯 GiST

通用搜索树
🎯 SP-GiST

空间分区树
🎯 BRIN

块范围索引

极小占用
✅ B-tree

默认推荐
需要支持 NULL?
🎯 Hash

PG10+ 安全

8.2 B-tree 索引(默认)

B-tree 是自平衡多路搜索树,支持等值和范围查询。

sql 复制代码
-- 创建基础 B-tree 索引
CREATE INDEX idx_users_email ON users(email);

-- 唯一索引
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- 复合索引(多列索引)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);

-- 部分索引(Partial Index)------ 只索引满足条件的行
CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';

-- 表达式索引(Expression Index)
CREATE INDEX idx_users_lower_email ON users(LOWER(email));
-- 然后可以使用:SELECT * FROM users WHERE LOWER(email) = 'alice@example.com';

-- 排序方向
CREATE INDEX idx_posts_created ON posts(created_at DESC NULLS LAST);

-- INCLUDE 子句(覆盖索引,PG11+)
CREATE INDEX idx_orders_user_include ON orders(user_id) INCLUDE (total, status);

B-tree 支持的操作符<, <=, =, >=, >, BETWEEN, IN, IS NULL, IS NOT NULL, LIKE 'abc%'(前缀匹配)

8.3 Hash 索引

仅支持 = 等值查询:

sql 复制代码
CREATE INDEX idx_users_email_hash ON users USING HASH (email);

💡 在 PostgreSQL 10 之前,Hash 索引不支持 WAL,不推荐使用。现在已安全。对单值等值查询,可能比 B-tree 略快,但 B-tree 仍是更通用的选择。

8.4 GIN 索引(倒排索引)

Generalized Inverted iNdex ------ 适用于一个字段包含多个值的场景:

sql 复制代码
-- 数组索引
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);
SELECT * FROM posts WHERE tags @> ARRAY['postgres'];

-- JSONB 索引
CREATE INDEX idx_products_data ON products USING GIN (data);
SELECT * FROM products WHERE data @> '{"brand":"Apple"}';

-- JSONB 专用操作符类(更小更快)
CREATE INDEX idx_products_data_path ON products USING GIN (data jsonb_path_ops);

-- 全文搜索索引
CREATE INDEX idx_articles_fts ON articles 
USING GIN (to_tsvector('english', title || ' ' || content));

8.5 GiST 索引

Generalized Search Tree ------ 通用平衡树:

sql 复制代码
-- 几何数据
CREATE INDEX idx_locations_point ON locations USING GIST (geom);

-- 范围类型
CREATE INDEX idx_reservations_period ON reservations USING GIST (period);

-- 全文搜索(也可用)
CREATE INDEX idx_articles_title_gist ON articles 
USING GIST (to_tsvector('english', title));

-- PostGIS 地理空间
CREATE INDEX idx_cities_geom ON cities USING GIST (geom);

8.6 BRIN 索引

Block Range INdex ------ 块范围索引,适用于大表、顺序相关的列

sql 复制代码
-- BRIN 索引极小(可能比 B-tree 小 1000 倍)
CREATE INDEX idx_logs_time_brin ON logs USING BRIN (created_at);

-- 对比大表上的 B-tree 索引(928MB) vs BRIN 索引(384KB)

适用场景

  • 时序数据(logs、metrics)
  • 按时间或序号插入的表
  • 数据在物理上已经有序

8.7 SP-GiST 索引

Space-Partitioned GiST ------ 空间分区树:

sql 复制代码
-- IP 路由
CREATE INDEX idx_servers_ip ON servers USING SPGIST (ip inet_ops);

-- 点数据(更快的 KNN)
CREATE INDEX idx_points ON points USING SPGIST (location);

8.8 索引使用建议

应该建索引

  • 主键、唯一键(自动创建)
  • 外键列
  • WHERE、JOIN、ORDER BY 中频繁使用的列
  • 选择性高的列(区分度 > 10%)

不建议建索引

  • 小表(几百行以内)
  • 频繁更新的列
  • 选择性很低(如性别)
  • 表中 NULL 值占多数

8.9 查看索引使用情况

sql 复制代码
-- 查看某表所有索引
\d+ tablename

-- 查看索引大小
SELECT 
    schemaname, 
    tablename, 
    indexname, 
    pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC;

-- 查看未使用的索引(可能需要删除)
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

九、MVCC 与事务隔离

9.1 什么是 MVCC

MVCC(Multi-Version Concurrency Control,多版本并发控制) 是 PostgreSQL 的核心并发机制:

每个事务看到的是数据库在某个时刻的"快照",读写不会互相阻塞。

核心思想:不是覆盖旧数据,而是保留多个版本,通过事务 ID 判断可见性。

9.2 MVCC 工作原理

每行数据都有隐藏字段

字段 说明
xmin 创建该行的事务 ID
xmax 删除/更新该行的事务 ID(0 表示未删除)
ctid 行物理位置(block, offset)

示例

sql 复制代码
-- 查看元组元信息
SELECT xmin, xmax, ctid, * FROM users;

UPDATE 操作流程(重要!):

复制代码
UPDATE users SET age = 26 WHERE id = 1;

PostgreSQL 不会直接修改原行,而是:

  1. 标记原行的 xmax = 当前事务 ID(逻辑删除)
  2. 插入一行新版本 ,其 xmin = 当前事务 ID
  3. 其他事务根据自己的快照,可能看到新版本或旧版本

这就是 PostgreSQL UPDATE 为什么会产生"死元组"和需要 VACUUM 的原因!
UPDATE users

SET age=26

WHERE id=1
UPDATE 后(事务 101)
Row v1 (dead)

xmin=100

xmax=101 🔴

id=1, age=25
Row v2 (live)

xmin=101 ✅

xmax=0

id=1, age=26
UPDATE 前
Row v1

xmin=100

xmax=0

id=1, age=25

可见性判断

  • 事务 ID = 99 的事务:看到 Row v1(xmin=100 尚未创建时自己已在运行,因此看不到...其实看情况,此处为示意)
  • 事务 ID = 102 的事务(102 > 101):看到 Row v2
  • 被标记为 dead 的行,等待 VACUUM 清理

9.3 VACUUM:死元组清理

由于 MVCC 会产生大量死元组(dead tuple),PostgreSQL 需要定期清理:

sql 复制代码
-- 手动 VACUUM(标记空间可重用)
VACUUM users;

-- VACUUM FULL(重写表,释放磁盘空间,但锁表!)
VACUUM FULL users;

-- VACUUM + 更新统计信息
VACUUM ANALYZE users;

-- 详细输出
VACUUM VERBOSE users;

-- 查看表的膨胀情况
SELECT schemaname, relname, n_live_tup, n_dead_tup, 
       round(n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

AutoVacuum(自动清理)默认开启,推荐永不关闭!

9.4 ACID 与事务

sql 复制代码
-- 基本事务
BEGIN;                              -- 或 START TRANSACTION;
INSERT INTO accounts VALUES (1, 1000);
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;                             -- 提交

-- 回滚
BEGIN;
DELETE FROM users;
ROLLBACK;                           -- 撤销操作

-- 保存点
BEGIN;
INSERT INTO logs VALUES (1, 'step1');
SAVEPOINT sp1;
INSERT INTO logs VALUES (2, 'step2');
ROLLBACK TO sp1;                    -- 只回滚到 sp1
INSERT INTO logs VALUES (3, 'step3');
COMMIT;

9.5 事务隔离级别

SQL 标准定义 4 个隔离级别,PostgreSQL 实际实现 3 个(Read Uncommitted 等同 Read Committed):
事务隔离级别(由弱到强)
📖 Read

Committed

(默认)
📘 Repeatable

Read

(快照隔离)
📚 Serializable

(可序列化)
性能 ⭐⭐⭐

隔离 ⭐
性能 ⭐⭐

隔离 ⭐⭐
性能 ⭐

隔离 ⭐⭐⭐

隔离级别 脏读 不可重复读 幻读 序列化异常
Read Uncommitted ❌ PG 无 ✓ 可能 ✓ 可能 ✓ 可能
Read Committed(默认) ✓ 可能 ✓ 可能 ✓ 可能
Repeatable Read ❌ PG 也防止 ✓ 可能
Serializable
sql 复制代码
-- 设置事务隔离级别
BEGIN ISOLATION LEVEL READ COMMITTED;    -- 默认
BEGIN ISOLATION LEVEL REPEATABLE READ;   -- 快照隔离(SI)
BEGIN ISOLATION LEVEL SERIALIZABLE;      -- 可序列化(SSI)

-- 全局修改默认级别
SET DEFAULT_TRANSACTION_ISOLATION = 'repeatable read';

异常类型解释

  • 脏读(Dirty Read) :读到其他事务未提交的数据。PostgreSQL 永远不会发生
  • 不可重复读(Non-Repeatable Read):同一事务两次读同一行,结果不同
  • 幻读(Phantom Read):同一事务两次范围查询,结果集不同
  • 序列化异常(Serialization Anomaly):并发事务执行结果无法等价于串行执行

9.6 显式锁定

sql 复制代码
-- 表级锁
LOCK TABLE users IN ACCESS EXCLUSIVE MODE;

-- 行级锁(SELECT FOR UPDATE)
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- 共享锁
SELECT * FROM accounts WHERE id = 1 FOR SHARE;

-- 跳过已锁定的行(队列处理)
SELECT * FROM tasks WHERE status = 'pending' 
ORDER BY created_at 
LIMIT 1 
FOR UPDATE SKIP LOCKED;    -- 并发消费队列

-- 咨询锁(Advisory Lock,应用级锁)
SELECT pg_advisory_lock(123);        -- 加锁
SELECT pg_advisory_unlock(123);      -- 释放

十、JSON/JSONB 全面使用

PostgreSQL 对 JSON 的支持堪称业界标杆,可用作高性能文档数据库

10.1 JSON 与 JSONB 的区别

特性 JSON JSONB
存储格式 原始文本 二进制
写入速度 ✓ 快 略慢(要解析)
查询速度 慢(每次解析)
保留空格
保留键顺序
保留重复键 ❌(保留最后一个)
支持索引 有限 GIN 索引

结论:生产环境 99% 场景应使用 JSONB。

10.2 基础使用

sql 复制代码
-- 建表
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    data JSONB
);

-- 插入
INSERT INTO products (data) VALUES 
('{
    "name": "iPhone 15 Pro",
    "brand": "Apple",
    "price": 999.99,
    "specs": {
        "color": "Graphite",
        "storage": "256GB"
    },
    "tags": ["smartphone", "iOS", "Apple"]
}');

10.3 JSONB 操作符

操作符 说明 示例
-> 取字段,返回 JSONB data -> 'name'"iPhone 15 Pro"
->> 取字段,返回 text data ->> 'name'iPhone 15 Pro
#> 按路径取值,返回 JSONB data #> '{specs,color}'
#>> 按路径取值,返回 text data #>> '{specs,color}'
@> 包含(LHS 包含 RHS) data @> '{"brand":"Apple"}'
<@ 被包含 '{"x":1}' <@ '{"x":1,"y":2}'
? 顶层键存在 data ? 'brand'
`? ` 任一键存在
?& 所有键存在 data ?& array['a','b']
@? jsonpath 匹配 data @? '$.price ? (@ > 500)'
@@ jsonpath 条件 data @@ '$.price > 500'
` `
- 删除键/元素 data - 'tags'
#- 按路径删除 data #- '{specs,color}'

10.4 JSONB 查询示例

sql 复制代码
-- 查询字段
SELECT data ->> 'name' AS name,
       (data ->> 'price')::NUMERIC AS price
FROM products;

-- 按嵌套字段过滤
SELECT * FROM products 
WHERE data #>> '{specs,color}' = 'Graphite';

-- 包含查询(最常用)
SELECT * FROM products WHERE data @> '{"brand":"Apple"}';

-- 多条件包含
SELECT * FROM products 
WHERE data @> '{"brand":"Apple", "specs":{"storage":"256GB"}}';

-- 数组元素查询
SELECT * FROM products WHERE data -> 'tags' ? 'smartphone';

-- JSONPath 查询(PG 12+)
SELECT jsonb_path_query(data, '$.tags[*]') FROM products;
SELECT * FROM products WHERE data @? '$.price ? (@ > 500)';

-- 递归展开 JSON 对象
SELECT key, value FROM products, jsonb_each(data);

-- 展开 JSON 数组
SELECT jsonb_array_elements_text(data -> 'tags') AS tag FROM products;

10.5 JSONB 修改

sql 复制代码
-- 添加/更新字段
UPDATE products 
SET data = jsonb_set(data, '{price}', '1099.99') 
WHERE id = 1;

-- 添加新字段(若不存在)
UPDATE products 
SET data = jsonb_set(data, '{discount}', '0.1', true) 
WHERE id = 1;

-- 合并对象
UPDATE products 
SET data = data || '{"on_sale": true, "sale_price": 899}'::jsonb 
WHERE id = 1;

-- 删除字段
UPDATE products SET data = data - 'discount' WHERE id = 1;

-- 删除路径
UPDATE products SET data = data #- '{specs,color}' WHERE id = 1;

-- 追加数组元素
UPDATE products 
SET data = jsonb_set(data, '{tags}', data->'tags' || '"new-tag"') 
WHERE id = 1;

10.6 JSONB 索引

sql 复制代码
-- 默认 GIN 索引(支持所有操作)
CREATE INDEX idx_products_data ON products USING GIN (data);

-- jsonb_path_ops(更小更快,但只支持 @>, @?, @@)
CREATE INDEX idx_products_data_path ON products USING GIN (data jsonb_path_ops);

-- 对特定字段建 B-tree 索引(用于排序或等值)
CREATE INDEX idx_products_brand ON products ((data ->> 'brand'));
CREATE INDEX idx_products_price ON products (((data ->> 'price')::numeric));

10.7 聚合函数

sql 复制代码
-- 将查询结果聚合为 JSON 数组
SELECT jsonb_agg(data) FROM products;

-- 按某字段分组聚合
SELECT 
    data ->> 'brand' AS brand,
    jsonb_agg(data ->> 'name') AS product_names
FROM products 
GROUP BY data ->> 'brand';

-- 从 key-value 对构建 JSON
SELECT jsonb_object_agg(id, data) FROM products;

-- 构建 JSON 对象
SELECT jsonb_build_object('id', 1, 'name', 'Alice', 'tags', ARRAY['a','b']);

-- 构建 JSON 数组
SELECT jsonb_build_array(1, 'text', true, NULL);

十一、高级特性

11.1 视图(View)

sql 复制代码
-- 普通视图(每次查询都执行底层 SQL)
CREATE VIEW active_users AS
SELECT id, username, email, created_at
FROM users
WHERE status = 'active';

-- 使用视图
SELECT * FROM active_users WHERE created_at > '2025-01-01';

-- 更新视图定义
CREATE OR REPLACE VIEW active_users AS ...;

-- 物化视图(Materialized View)------ 预计算并存储结果
CREATE MATERIALIZED VIEW daily_sales AS
SELECT 
    DATE(created_at) AS day,
    COUNT(*) AS order_count,
    SUM(total) AS total_sales
FROM orders
GROUP BY DATE(created_at);

-- 刷新物化视图
REFRESH MATERIALIZED VIEW daily_sales;

-- 并发刷新(不阻塞读,需要唯一索引)
CREATE UNIQUE INDEX ON daily_sales(day);
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales;

11.2 存储过程与函数

PostgreSQL 支持多种过程语言:PL/pgSQL(默认)、PL/PythonPL/PerlPL/TclPL/Java 等。

sql 复制代码
-- 创建函数(SQL 语言)
CREATE OR REPLACE FUNCTION get_user_email(user_id INTEGER)
RETURNS TEXT AS $$
    SELECT email FROM users WHERE id = user_id;
$$ LANGUAGE sql STABLE;

-- 创建函数(PL/pgSQL)
CREATE OR REPLACE FUNCTION transfer_money(
    from_id INTEGER, 
    to_id   INTEGER, 
    amount  NUMERIC
) 
RETURNS BOOLEAN AS $$
DECLARE
    from_balance NUMERIC;
BEGIN
    -- 检查余额
    SELECT balance INTO from_balance FROM accounts WHERE id = from_id;
    IF from_balance < amount THEN
        RAISE EXCEPTION '余额不足: %', from_balance;
    END IF;
    
    -- 执行转账
    UPDATE accounts SET balance = balance - amount WHERE id = from_id;
    UPDATE accounts SET balance = balance + amount WHERE id = to_id;
    
    -- 记录日志
    INSERT INTO logs (message) 
    VALUES (format('Transfer %s from %s to %s', amount, from_id, to_id));
    
    RETURN TRUE;
EXCEPTION
    WHEN OTHERS THEN
        RAISE NOTICE 'Error: %', SQLERRM;
        RETURN FALSE;
END;
$$ LANGUAGE plpgsql;

-- 调用
SELECT transfer_money(1, 2, 100.00);

-- 返回表结构
CREATE OR REPLACE FUNCTION get_user_orders(user_id INTEGER)
RETURNS TABLE(id BIGINT, total NUMERIC, status TEXT) AS $$
    SELECT id, total, status FROM orders WHERE orders.user_id = $1;
$$ LANGUAGE sql;

SELECT * FROM get_user_orders(1);

-- 存储过程(PG11+,支持事务控制)
CREATE OR REPLACE PROCEDURE batch_insert(data INTEGER[])
AS $$
BEGIN
    FOR i IN array_lower(data, 1)..array_upper(data, 1) LOOP
        INSERT INTO logs VALUES (data[i]);
        IF i % 1000 = 0 THEN
            COMMIT;       -- 批量提交
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

CALL batch_insert(ARRAY[1,2,3,4,5]);

11.3 触发器(Trigger)

sql 复制代码
-- 1. 创建触发器函数
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 2. 创建触发器
CREATE TRIGGER trg_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_modified_column();

-- 条件触发器
CREATE TRIGGER trg_expensive_orders
AFTER INSERT ON orders
FOR EACH ROW
WHEN (NEW.total > 10000)
EXECUTE FUNCTION notify_manager();

-- 审计触发器示例
CREATE OR REPLACE FUNCTION audit_changes() RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO audit_log (table_name, operation, old_data, new_data, user_name, ts)
    VALUES (
        TG_TABLE_NAME,
        TG_OP,
        row_to_json(OLD),
        row_to_json(NEW),
        current_user,
        NOW()
    );
    RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_users_audit
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_changes();

11.4 窗口函数(Window Functions)

窗口函数可以在不合并行的情况下对一组行进行计算:

sql 复制代码
-- ROW_NUMBER、RANK、DENSE_RANK
SELECT 
    department,
    name,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num,
    RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank_num,
    DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank
FROM employees;

-- 部门工资排名前 3
SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
    FROM employees
) t WHERE rn <= 3;

-- LAG / LEAD(查看前/后一行)
SELECT 
    order_date,
    revenue,
    LAG(revenue, 1)  OVER (ORDER BY order_date) AS prev_day_revenue,
    LEAD(revenue, 1) OVER (ORDER BY order_date) AS next_day_revenue,
    revenue - LAG(revenue) OVER (ORDER BY order_date) AS change
FROM daily_revenue;

-- 移动平均
SELECT 
    date, 
    temperature,
    AVG(temperature) OVER (
        ORDER BY date 
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS avg_7day
FROM weather;

-- 累计求和
SELECT 
    date, 
    amount,
    SUM(amount) OVER (ORDER BY date) AS running_total,
    SUM(amount) OVER (PARTITION BY category ORDER BY date) AS category_total
FROM transactions;

-- FIRST_VALUE / LAST_VALUE / NTH_VALUE
SELECT 
    category,
    product,
    price,
    FIRST_VALUE(product) OVER (PARTITION BY category ORDER BY price DESC) AS most_expensive,
    LAST_VALUE(product) OVER (
        PARTITION BY category ORDER BY price DESC 
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS cheapest
FROM products;

-- NTILE(分组分桶)
SELECT 
    name,
    score,
    NTILE(4) OVER (ORDER BY score DESC) AS quartile
FROM students;

11.5 CTE(公共表表达式)

sql 复制代码
-- 基础 CTE
WITH active_users AS (
    SELECT id, username FROM users WHERE status = 'active'
),
user_orders AS (
    SELECT user_id, COUNT(*) AS order_count 
    FROM orders 
    GROUP BY user_id
)
SELECT au.username, COALESCE(uo.order_count, 0) AS orders
FROM active_users au
LEFT JOIN user_orders uo ON au.id = uo.user_id;

-- 递归 CTE(经典用法:员工树形结构)
WITH RECURSIVE employee_tree AS (
    -- 初始查询(锚定部分)
    SELECT id, name, manager_id, 1 AS level
    FROM employees
    WHERE manager_id IS NULL
    
    UNION ALL
    
    -- 递归部分
    SELECT e.id, e.name, e.manager_id, et.level + 1
    FROM employees e
    INNER JOIN employee_tree et ON e.manager_id = et.id
)
SELECT id, name, level, repeat('  ', level - 1) || name AS tree_view
FROM employee_tree
ORDER BY level, name;

-- 递归 CTE:生成日期序列
WITH RECURSIVE dates AS (
    SELECT DATE '2026-01-01' AS d
    UNION ALL
    SELECT d + 1 FROM dates WHERE d < '2026-12-31'
)
SELECT * FROM dates;

-- 修改操作在 CTE 中(PG 9.1+)
WITH deleted AS (
    DELETE FROM logs WHERE created_at < NOW() - INTERVAL '30 days' RETURNING *
)
INSERT INTO logs_archive SELECT * FROM deleted;

11.6 全文搜索

PostgreSQL 内置强大的全文搜索能力:

sql 复制代码
-- 创建 tsvector 列
ALTER TABLE articles ADD COLUMN search_vector tsvector;

-- 更新 tsvector
UPDATE articles 
SET search_vector = 
    setweight(to_tsvector('english', COALESCE(title, '')), 'A') ||
    setweight(to_tsvector('english', COALESCE(content, '')), 'B');

-- 创建 GIN 索引
CREATE INDEX idx_articles_search ON articles USING GIN (search_vector);

-- 查询
SELECT title, ts_rank(search_vector, query) AS rank
FROM articles, to_tsquery('english', 'postgres & (database | mvcc)') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 10;

-- 高亮显示搜索结果
SELECT ts_headline('english', content, to_tsquery('english', 'postgres'))
FROM articles WHERE search_vector @@ to_tsquery('english', 'postgres');

-- 中文分词(需要 zhparser 扩展)
CREATE EXTENSION zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese 
    ADD MAPPING FOR n,v,a,i,e,l WITH simple;
SELECT to_tsvector('chinese', 'PostgreSQL 是世界上最先进的开源数据库');

11.7 分区表(Declarative Partitioning)

适用于大表(千万级以上),按时间或某字段分区:

sql 复制代码
-- 范围分区(按时间)
CREATE TABLE logs (
    id BIGSERIAL,
    created_at TIMESTAMPTZ NOT NULL,
    message TEXT
) PARTITION BY RANGE (created_at);

-- 创建分区
CREATE TABLE logs_2026_01 PARTITION OF logs 
    FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

CREATE TABLE logs_2026_02 PARTITION OF logs 
    FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');

-- 默认分区(接收未匹配行)
CREATE TABLE logs_default PARTITION OF logs DEFAULT;

-- 列表分区
CREATE TABLE users (
    id SERIAL,
    country TEXT,
    name TEXT
) PARTITION BY LIST (country);

CREATE TABLE users_asia PARTITION OF users 
    FOR VALUES IN ('CN', 'JP', 'KR', 'SG');
CREATE TABLE users_europe PARTITION OF users 
    FOR VALUES IN ('DE', 'FR', 'UK');

-- 哈希分区
CREATE TABLE sessions (
    id UUID,
    data JSONB
) PARTITION BY HASH (id);

CREATE TABLE sessions_p0 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE sessions_p1 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE sessions_p2 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE sessions_p3 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 3);

-- 查看分区
\d+ logs

分区的好处

  • ✅ 查询可自动跳过不相关分区(分区剪枝)
  • ✅ 旧数据删除快:DROP TABLE logs_2020_01;
  • ✅ 分区级并行查询
  • ✅ 每个分区可独立 VACUUM、REINDEX

11.8 行级安全(Row-Level Security)

sql 复制代码
-- 开启 RLS
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

-- 创建策略
CREATE POLICY user_policy ON documents
FOR ALL
TO application_user
USING (owner_id = current_setting('app.current_user_id')::INTEGER);

-- 在应用层设置当前用户
SET app.current_user_id = '42';
SELECT * FROM documents;  -- 只返回 owner_id = 42 的记录

-- 只读策略
CREATE POLICY public_read ON documents 
FOR SELECT TO public 
USING (is_public = true);

十二、性能调优

12.1 核心内存参数(16GB 内存示例)

conf 复制代码
# postgresql.conf 推荐配置

# 共享内存缓冲(物理内存的 25%)
shared_buffers = 4GB

# 查询工作内存
work_mem = 16MB                     # 排序/哈希的内存
hash_mem_multiplier = 2.0           # PG15+,哈希操作内存倍数

# 维护工作内存
maintenance_work_mem = 1GB          # VACUUM、CREATE INDEX 用

# OS 缓存大小估算(物理内存的 50%~75%)
effective_cache_size = 12GB

# WAL
wal_buffers = 16MB
wal_compression = on                # 压缩 WAL
max_wal_size = 4GB
min_wal_size = 512MB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9

# I/O 并发(SSD 推荐 200+,HDD 推荐 2)
effective_io_concurrency = 200

# 随机读代价(SSD 1.1,HDD 4.0)
random_page_cost = 1.1

# PG 18 新增异步 I/O
io_method = worker                  # 默认:worker | sync | io_uring
io_workers = 3                      # 默认 3

# 并行查询
max_parallel_workers = 8
max_parallel_workers_per_gather = 4
max_worker_processes = 8

# 连接
max_connections = 200

12.2 使用 EXPLAIN 分析查询

SQL 查询执行流程
基于统计信息

pg_statistic
成本估算
SQL 语句
Parser

语法分析
Analyzer

语义分析
Rewriter

规则重写
Planner/Optimizer

生成执行计划
Executor

执行器
结果返回
统计信息
Cost-based

Optimizer

EXPLAIN最重要的性能分析工具

sql 复制代码
-- 基础 EXPLAIN:显示查询计划
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';

-- EXPLAIN ANALYZE:实际执行并显示耗时
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'alice@example.com';

-- 详细格式(推荐)
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT JSON) 
SELECT * FROM users WHERE email = 'alice@example.com';

执行计划示例解读

复制代码
Index Scan using idx_users_email on users  (cost=0.28..8.30 rows=1 width=100)
                                            ↑           ↑         ↑       ↑
                                  启动成本  总成本    估算行数   行宽度
  Index Cond: (email = 'alice@example.com'::text)
  Buffers: shared hit=4                    ← 缓冲区访问(hit 是内存命中)
  Planning Time: 0.1 ms
  Execution Time: 0.05 ms

常见扫描类型

扫描类型 说明 性能
Seq Scan 全表扫描 小表快,大表慢
Index Scan 索引扫描 很快
Index Only Scan 仅索引扫描(含数据) 最快
Bitmap Index Scan 位图索引扫描 多条件适合
Parallel Seq Scan 并行全表扫描 大表加速

常见连接类型

类型 说明 适用场景
Nested Loop 嵌套循环 小数据集
Hash Join 哈希连接 等值连接,中等数据
Merge Join 归并连接 已排序大数据

12.3 慢查询分析

启用慢查询日志

conf 复制代码
# postgresql.conf
log_min_duration_statement = 1000     # 记录 >1s 的查询
log_statement = 'all'                 # 记录所有语句(开发环境)

使用 pg_stat_statements 扩展

sql 复制代码
-- 安装扩展
CREATE EXTENSION pg_stat_statements;

-- 配置(postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'
-- pg_stat_statements.track = all

-- 查看最慢的 10 个查询
SELECT 
    query,
    calls,
    total_exec_time / 1000 AS total_sec,
    mean_exec_time AS avg_ms,
    rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

-- 重置统计
SELECT pg_stat_statements_reset();

12.4 常见性能优化技巧

1. 合理使用索引
sql 复制代码
-- ❌ 不走索引:函数作用于列
SELECT * FROM users WHERE LOWER(email) = 'alice@example.com';

-- ✅ 解决:建表达式索引
CREATE INDEX idx_users_email_lower ON users (LOWER(email));

-- ❌ 不走索引:隐式类型转换
SELECT * FROM users WHERE id = '123';  -- id 是 INT,字符串比较

-- ✅ 正确
SELECT * FROM users WHERE id = 123;
2. 避免 SELECT *
sql 复制代码
-- ❌ 返回所有列,浪费网络和内存
SELECT * FROM huge_table;

-- ✅ 只取需要的列
SELECT id, name FROM huge_table;
3. 使用 EXISTS 代替 IN(大数据集)
sql 复制代码
-- 数据集大时更快
SELECT * FROM users u 
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
4. 批量操作优于循环
sql 复制代码
-- ❌ 循环单条插入
FOR i IN 1..10000 LOOP INSERT INTO logs VALUES (i); END LOOP;

-- ✅ 批量插入
INSERT INTO logs SELECT generate_series(1, 10000);

-- ✅ 使用 COPY(最快)
COPY logs FROM '/path/to/data.csv' CSV;
5. 定期 VACUUM 和 ANALYZE
sql 复制代码
-- 更新统计信息(优化器依赖此信息)
ANALYZE users;

-- 全库 ANALYZE
ANALYZE;

-- 查看表是否需要 VACUUM
SELECT relname, n_dead_tup, n_live_tup,
       round(100 * n_dead_tup / nullif(n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
ORDER BY n_dead_tup DESC;

12.5 连接池(Connection Pooling)

PostgreSQL 的每个连接都是一个进程,建议使用连接池:

PgBouncer - 轻量级连接池(推荐):

ini 复制代码
# pgbouncer.ini
[databases]
mydb = host=localhost port=5432 dbname=mydb

[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = scram-sha-256
pool_mode = transaction      # transaction / session / statement
max_client_conn = 1000
default_pool_size = 25

十三、备份与恢复

备份策略全景
PostgreSQL 备份体系
适合
适合
适合
🔶 时点恢复 PITR
基础备份

Base Backup
任意时点恢复
持续归档 WAL

archive_command
🔸 物理备份
pg_basebackup

全库二进制拷贝
pgBackRest

企业级增量备份
🔹 逻辑备份
pg_dump

单库/单表

SQL 或 custom 格式
pg_dumpall

全局对象 + 所有库
小中数据量

跨版本迁移

单表还原
大数据量

快速恢复

搭建从库
灾难恢复

误操作回溯

合规审计

13.1 逻辑备份(pg_dump / pg_dumpall)

适用于:中小数据量、跨版本迁移、单库/单表备份。

bash 复制代码
# 备份单个数据库(SQL 格式)
pg_dump -U postgres -d mydb -f mydb.sql

# 备份为自定义格式(推荐,可压缩可并行)
pg_dump -U postgres -Fc -d mydb -f mydb.dump

# 备份指定 schema 或表
pg_dump -U postgres -d mydb -n public -t users -f users.sql

# 只备份数据(不含 DDL)
pg_dump -U postgres --data-only -d mydb -f data.sql

# 只备份结构
pg_dump -U postgres --schema-only -d mydb -f schema.sql

# 并行备份(仅目录格式)
pg_dump -U postgres -Fd -j 4 -d mydb -f mydb_backup/

# 备份所有数据库 + 全局对象(用户、权限等)
pg_dumpall -U postgres -f all.sql

# 仅备份全局对象
pg_dumpall -U postgres --globals-only -f globals.sql

恢复

bash 复制代码
# 恢复 SQL 格式
psql -U postgres -d mydb -f mydb.sql

# 恢复自定义格式
pg_restore -U postgres -d mydb mydb.dump

# 并行恢复
pg_restore -U postgres -d mydb -j 4 mydb.dump

# 只恢复特定对象
pg_restore -U postgres -d mydb -t users mydb.dump

# 清理已存在对象后再恢复
pg_restore -U postgres -d mydb --clean --if-exists mydb.dump

13.2 物理备份(pg_basebackup)

适用于:大数据量、完整实例备份、用于流复制 / PITR。

bash 复制代码
# 基础备份
pg_basebackup \
    -h localhost \
    -U replication_user \
    -D /backup/base \
    -Fp \                   # plain 格式
    -Xs \                   # 包含 WAL 流
    -P \                    # 显示进度
    -v                      # 详细输出

# 压缩备份
pg_basebackup -D /backup/base -Ft -z -P
# -Ft: tar 格式
# -z: 压缩

13.3 PITR(Point-in-Time Recovery)

时点恢复 = 基础备份 + 归档 WAL 日志

conf 复制代码
# postgresql.conf 启用归档
wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/%f'
# 或用 pg_receivewal / barman / wal-e / pgbackrest

恢复到指定时间点

bash 复制代码
# 1. 停止 PostgreSQL,替换数据目录为备份
# 2. 创建恢复配置(PG12+ 在 postgresql.conf 中)
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2026-04-17 10:30:00'

# 3. 创建 recovery.signal 文件
touch $PGDATA/recovery.signal

# 4. 启动 PostgreSQL
pg_ctl start

# 5. 恢复完成后
SELECT pg_wal_replay_resume();

13.4 专业备份工具推荐

工具 特点
pgBackRest 企业级,支持并行、增量、加密
Barman 备份与灾难恢复管理
WAL-G 云原生,支持 S3、GCS
pg_probackup Postgres Professional 出品

十四、复制与高可用

14.1 流复制(Physical Replication)

主从架构,通过 WAL 日志实时同步
🔵 Standby 2 从库(只读)
🔵 Standby 1 从库(只读)
🟢 Primary 主库(可读可写)
流式传输 WAL
流式传输 WAL
应用写入
WAL 生成器
WAL Sender

进程
WAL Receiver
WAL Apply
只读查询
WAL Receiver
WAL Apply
只读查询

主库配置(postgresql.conf):

conf 复制代码
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
synchronous_commit = on      # on/off/remote_apply/remote_write

主库 pg_hba.conf

复制代码
host replication replicator 192.168.1.0/24 scram-sha-256

主库创建复制用户

sql 复制代码
CREATE USER replicator WITH REPLICATION PASSWORD 'secretpwd';

从库初始化

bash 复制代码
# 使用 pg_basebackup 克隆主库
pg_basebackup -h primary.host -U replicator -D /var/lib/postgresql/18/main -Fp -Xs -R -P
# -R: 自动创建 standby.signal 和 primary_conninfo

从库 postgresql.auto.conf(自动生成):

conf 复制代码
primary_conninfo = 'host=primary.host port=5432 user=replicator password=...'

验证复制状态

sql 复制代码
-- 主库查看从库信息
SELECT client_addr, state, sync_state FROM pg_stat_replication;

-- 从库查看是否接收 WAL
SELECT pg_is_in_recovery();   -- 应返回 t
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();

14.2 逻辑复制(Logical Replication)

基于逻辑解码,支持跨版本、跨平台、部分表复制

sql 复制代码
-- 在主库
ALTER SYSTEM SET wal_level = 'logical';
-- 重启 PostgreSQL

CREATE PUBLICATION my_pub FOR TABLE users, orders;

-- 在订阅库
CREATE SUBSCRIPTION my_sub 
CONNECTION 'host=primary port=5432 dbname=mydb user=replicator password=...' 
PUBLICATION my_pub;

-- 查看状态
SELECT * FROM pg_stat_replication;       -- 主库
SELECT * FROM pg_subscription;           -- 订阅库
SELECT * FROM pg_stat_subscription;

逻辑复制用途

  • 跨 PG 大版本升级(零停机)
  • 多数据中心双活
  • 数据汇聚(多个源 → 一个目标)
  • 部分表同步

14.3 高可用方案

方案 说明
Patroni 基于 etcd/consul/zookeeper 的自动故障转移
repmgr 简单的复制管理与故障转移
pg_auto_failover Citus 出品,易部署
Pacemaker + Corosync 传统 Linux HA 方案
PgPool-II 连接池 + 负载均衡 + 故障转移
Stolon Kubernetes 原生方案

十五、扩展生态

PostgreSQL 最强大的地方在于扩展(Extension)生态 ,通过简单的 CREATE EXTENSION 即可获得强大功能。
🐘 PostgreSQL 18

关系数据库核心
🤖 AI 场景
⏱️ 时序场景
🌍 地理场景
🔍 搜索场景
🔧 运维与性能
🔐 安全与加密
📊 分布式
pgvector

向量搜索
pgvectorscale

DiskANN 大规模
pgai

SQL 调用 LLM
TimescaleDB

hypertable 时序
PostGIS

世界级 GIS
pgRouting

路径规划
pg_trgm

模糊匹配
pg_bm25

全文排名
pg_stat_statements

慢查询分析
pg_cron

定时任务
pg_repack

在线压缩
pg_partman

分区管理
pgcrypto

加密函数
pgaudit

审计日志
Citus

分布式 PG
pglogical

逻辑复制增强

💡 2026 年趋势:越来越多团队使用 "一个 PostgreSQL 代替多个专用数据库" 的架构,取代 Pinecone(向量)、Elasticsearch(搜索)、Redis(缓存)、InfluxDB(时序)等。

15.1 PostGIS - 地理信息系统

PostgreSQL 成为世界级开源 GIS 数据库

sql 复制代码
CREATE EXTENSION postgis;

-- 创建地理点
CREATE TABLE cities (
    id SERIAL PRIMARY KEY,
    name TEXT,
    geom GEOMETRY(Point, 4326)     -- WGS84 经纬度
);

INSERT INTO cities VALUES 
    (1, '北京', ST_GeomFromText('POINT(116.40 39.90)', 4326)),
    (2, '上海', ST_GeomFromText('POINT(121.47 31.23)', 4326)),
    (3, '广州', ST_GeomFromText('POINT(113.26 23.13)', 4326));

-- GIST 索引
CREATE INDEX idx_cities_geom ON cities USING GIST (geom);

-- 查询 500km 内的城市
SELECT name, ST_Distance(geom::geography, ST_MakePoint(116.40, 39.90)::geography) / 1000 AS km
FROM cities
WHERE ST_DWithin(
    geom::geography,
    ST_MakePoint(116.40, 39.90)::geography,
    500000
)
ORDER BY km;

-- 支持 300+ 空间函数:距离、缓冲区、交集、面积等

15.2 pgvector - AI 向量数据库

AI 时代最热门的扩展,将 Postgres 变为向量数据库

sql 复制代码
CREATE EXTENSION vector;

-- 创建向量列
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    embedding VECTOR(1536)         -- OpenAI ada-002 维度
);

-- 创建 HNSW 索引(近似最近邻搜索)
CREATE INDEX ON documents 
USING hnsw (embedding vector_cosine_ops);

-- 插入向量
INSERT INTO documents VALUES 
    (1, '...', '[0.1, 0.2, ...]'::vector);

-- 最近邻查询
SELECT id, content, 1 - (embedding <=> '[0.1, 0.2, ...]'::vector) AS similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

-- 支持的距离:
-- <->  L2 距离
-- <#>  内积(负)
-- <=>  余弦距离

15.3 TimescaleDB - 时序数据库

Postgres 的时序数据库扩展,专为 IoT / 监控 / 金融设计

sql 复制代码
CREATE EXTENSION timescaledb;

CREATE TABLE metrics (
    time TIMESTAMPTZ NOT NULL,
    device_id INTEGER,
    temperature DOUBLE PRECISION,
    humidity DOUBLE PRECISION
);

-- 创建 hypertable(自动按时间分区)
SELECT create_hypertable('metrics', 'time');

-- 查询性能可比普通表快 10-1000 倍
-- 支持 90%+ 数据压缩
SELECT add_compression_policy('metrics', INTERVAL '7 days');

-- 连续聚合(实时汇总)
CREATE MATERIALIZED VIEW daily_avg 
WITH (timescaledb.continuous) AS
SELECT 
    time_bucket('1 day', time) AS day,
    device_id,
    AVG(temperature) AS avg_temp
FROM metrics
GROUP BY day, device_id;

15.4 pg_stat_statements - 性能分析(必装)

sql 复制代码
CREATE EXTENSION pg_stat_statements;

-- 查看慢查询 TOP 10
SELECT substring(query, 1, 100) AS query,
       calls, 
       round(total_exec_time::numeric, 2) AS total_ms,
       round(mean_exec_time::numeric, 2) AS avg_ms,
       rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

15.5 其他常用扩展

扩展 功能
pg_trgm 模糊文本匹配(三字母索引)
pgcrypto 加密函数(AES、哈希)
uuid-ossp UUID 生成
hstore 键值对数据类型
ltree 树形结构数据
pg_cron 数据库内定时任务
pg_repack 在线压缩表,不阻塞
pgvectorscale pgvector 的大规模增强
pgai 数据库内调用 AI 模型
Citus 分布式 PostgreSQL
pg_partman 分区管理
pglogical 增强逻辑复制
foreign data wrappers 读取其他数据源(MySQL、Mongo、CSV 等)

15.6 安装扩展

sql 复制代码
-- 查看可用扩展
SELECT * FROM pg_available_extensions ORDER BY name;

-- 安装扩展
CREATE EXTENSION IF NOT EXISTS extension_name;

-- 升级扩展
ALTER EXTENSION extension_name UPDATE;

-- 查看已安装扩展
\dx

-- 删除扩展
DROP EXTENSION IF EXISTS extension_name CASCADE;

十六、PostgreSQL 18 新特性

PostgreSQL 18 于 2025 年 9 月 25 日 发布,是一个里程碑版本,包含 3,000+ commits、200+ 作者的贡献。

16.1 异步 I/O(AIO)------ 性能飞跃

PostgreSQL 18 最重磅的特性 :官方宣称可带来 最高 3 倍 的读性能提升。

传统同步 I/O(PostgreSQL 17 及以前):
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 读请求1 - 等待 读请求2 - 等待 读请求3 - 等待 读请求4 - 等待 处理数据 Query Backend 同步 I/O:串行等待

异步 I/O(PostgreSQL 18+):
0 0 1 1 2 2 3 3 4 4 5 5 6 提交所有读请求 并发等待 读请求1 读请求2 读请求3 读请求4 处理数据 Query Backend I/O Workers 异步 I/O:并发请求

💡 异步 I/O 可带来最高 3 倍的读性能提升,尤其在云环境中效果最为显著。

配置

conf 复制代码
io_method = worker         # 默认,使用 I/O worker 进程
io_method = io_uring       # Linux 5.1+ 专用,最高性能
io_method = sync           # 传统同步行为
io_workers = 3             # I/O worker 数量

监控

sql 复制代码
-- PG 18 新增系统视图
SELECT * FROM pg_aios;

-- 查看 I/O worker 进程
SELECT pid, backend_type FROM pg_stat_activity 
WHERE backend_type = 'io worker';

适用场景(AIO 收益最大):

  • ☁️ 云环境(EBS、GCP Persistent Disk)
  • 📊 大表顺序扫描
  • 📈 位图堆扫描(Bitmap Heap Scan)
  • 🧹 VACUUM 操作

16.2 UUIDv7 - 时间有序的 UUID

传统 UUIDv4 是完全随机的,作为主键会导致:

  • ❌ 索引位置跳跃,缓存局部性差
  • ❌ 随机插入导致 B-tree 分裂频繁
  • ❌ 压缩效果差

UUIDv7 = 时间戳 + 随机,既保证全局唯一,又保持时间顺序:

sql 复制代码
-- PG 18 新增函数
SELECT uuidv7();              -- 生成时间有序 UUID
SELECT uuidv4();              -- 同 gen_random_uuid()

-- 作为主键(强烈推荐)
CREATE TABLE events (
    id UUID DEFAULT uuidv7() PRIMARY KEY,
    data JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 性能对比:UUIDv7 vs UUIDv4 在大表插入时快数倍

16.3 虚拟生成列(Virtual Generated Columns)

PG 18 起,生成列默认为虚拟(查询时计算,不占存储):

sql 复制代码
CREATE TABLE user_profiles (
    user_id SERIAL PRIMARY KEY,
    settings JSONB,
    -- 默认 VIRTUAL:不存储,查询时计算
    username VARCHAR(100) GENERATED ALWAYS AS (settings ->> 'username') VIRTUAL,
    -- 或 STORED:存储,可索引
    email VARCHAR(100) GENERATED ALWAYS AS (settings ->> 'email') STORED
);

SELECT username FROM user_profiles;  -- 自动从 settings 计算

16.4 OAuth 2.0 认证

支持与 Okta、Keycloak、Auth0 等 SSO 系统集成

conf 复制代码
# pg_hba.conf
host  all  all  0.0.0.0/0  oauth  issuer="https://auth.example.com" scope="postgres"

不再需要为数据库维护长期密码,直接用企业 SSO 登录。

16.5 跳过扫描(Skip Scan)

允许多列 B-tree 索引跳过前缀列

sql 复制代码
-- 索引:(region, status, created_at)
CREATE INDEX idx_orders ON orders (region, status, created_at);

-- 即使不指定 region,也可走索引(PG 18+)
SELECT * FROM orders WHERE status = 'shipped';

16.6 RETURNING 支持 OLD 和 NEW

sql 复制代码
-- PG 18+ 可同时返回修改前后的值
UPDATE users 
SET salary = salary * 1.1 
WHERE department = 'engineering' 
RETURNING OLD.salary AS old_salary, NEW.salary AS new_salary;

16.7 时态约束(Temporal Constraints)

sql 复制代码
-- WITHOUT OVERLAPS:同一资源在时间段内不重叠
CREATE TABLE room_bookings (
    room_id INTEGER,
    period TSTZRANGE,
    PRIMARY KEY (room_id, period WITHOUT OVERLAPS)  -- PG 18+
);
-- 保证同一房间不会被双重预订

16.8 其他重要改进

  • 🔧 pg_upgrade 性能大幅提升,支持并行检查、--swap 模式
  • 📝 升级后统计信息保留(无需重新 ANALYZE)
  • 🌐 新 wire protocol 3.2(2003 年以来首次大版本升级)
  • 🔐 MD5 密码认证正式弃用
  • ✅ initdb 默认开启数据校验和

十七、最佳实践

17.1 设计层面

主键推荐用 BIGINT 或 UUID v7

sql 复制代码
-- 不推荐:INT 在大表上会溢出(21 亿)
id SERIAL PRIMARY KEY

-- 推荐
id BIGSERIAL PRIMARY KEY
-- 或
id UUID PRIMARY KEY DEFAULT uuidv7()

时间戳用 TIMESTAMPTZ

sql 复制代码
-- 带时区,自动处理,跨时区正确
created_at TIMESTAMPTZ DEFAULT NOW()

合理使用 JSONB

  • 适合:半结构化、schema 变化频繁
  • 不适合:核心业务实体(用户、订单)

用 NUMERIC 而不是 FLOAT

sql 复制代码
-- ❌ 浮点数有精度问题
total FLOAT

-- ✅ 精确小数
total NUMERIC(10, 2)

17.2 SQL 编写

使用参数化查询(防 SQL 注入)

python 复制代码
# ✅ 参数化
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

# ❌ 字符串拼接
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")

INSERT ... ON CONFLICT(UPSERT)

sql 复制代码
INSERT INTO users (email, name) 
VALUES ('a@b.com', 'Alice')
ON CONFLICT (email) 
DO UPDATE SET name = EXCLUDED.name;

LIMIT 大数据分页用 keyset pagination

sql 复制代码
-- ❌ OFFSET 大时慢
SELECT * FROM posts ORDER BY id LIMIT 20 OFFSET 100000;

-- ✅ 基于上一页最后一个 id
SELECT * FROM posts WHERE id > 100000 ORDER BY id LIMIT 20;

17.3 索引策略

遵循最左前缀原则

sql 复制代码
-- 索引 (a, b, c)
-- 可以用的查询:WHERE a=?  /  WHERE a=? AND b=?  /  WHERE a=? AND b=? AND c=?
-- 不能用的:WHERE b=?  /  WHERE c=?

高频查询列优先

定期检查无用索引

sql 复制代码
SELECT * FROM pg_stat_user_indexes WHERE idx_scan = 0;

17.4 运维层面

监控必备指标

  • 连接数 / 活跃连接
  • 缓存命中率(>99% 为优)
  • 死元组数
  • WAL 生成速率
  • 复制延迟

缓存命中率查询

sql 复制代码
SELECT 
    sum(heap_blks_read) AS disk_read,
    sum(heap_blks_hit)  AS buffer_hit,
    round(sum(heap_blks_hit)::NUMERIC / NULLIF(sum(heap_blks_hit + heap_blks_read), 0) * 100, 2) AS hit_ratio
FROM pg_statio_user_tables;

定期备份 + 定期演练恢复

使用连接池(PgBouncer / Pgpool-II)

监控工具:pgwatch2、pghero、pgAdmin、Grafana + prometheus-postgres-exporter

17.5 安全最佳实践

最小权限原则

sql 复制代码
-- 不要让应用使用 postgres 超级用户
CREATE USER app_user WITH PASSWORD 'strong_password';
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;

启用 SSL

conf 复制代码
# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'

pg_hba.conf 使用 scram-sha-256

禁用超级用户远程登录

启用审计日志(pgaudit 扩展)


十八、与 MySQL 对比

整体对比雷达
🐬 MySQL 优势场景
Web OLTP 主流
LAMP/LEMP 栈
简单读密集
运维人员熟悉
🐘 PostgreSQL 优势场景
复杂查询
JSON/地理/向量
严格一致性
扩展生态
分析型 OLAP
并行查询

维度 PostgreSQL MySQL
标准兼容性 SQL:2023 的 170/177 项 较弱
默认存储引擎 统一(堆表 + MVCC) InnoDB(可切换)
ACID 支持 核心架构全支持 仅 InnoDB 支持
默认隔离级别 Read Committed Repeatable Read
MVCC 实现 行多版本,需 VACUUM Undo Log,自动清理
JSON 支持 JSONB(二进制 + GIN 索引) JSON(文本)
地理空间 PostGIS(业界最强) MySQL Spatial(有限)
全文搜索 内置 + tsvector 有(有限)
并发性 读写不互相阻塞 依赖锁
并行查询 ✅ 强大 仅 MySQL 8+ 部分支持
分区 声明式 + 灵活 不如 PG 灵活
CTE 与窗口函数 ✅ 完整 MySQL 8+ 才支持
物化视图 ✅ 原生支持 ❌ 需自己实现
扩展性 超强(PostGIS、TimescaleDB、pgvector...) 一般
子查询优化 优秀 较弱(曾是痛点)
复制 流复制 + 逻辑复制 Binlog 复制
生态 快速增长,AI 时代新宠 成熟,Web 主流
适用场景 复杂查询、GIS、AI、数据分析 Web 应用、LAMP 栈

选型建议

  • 需要复杂查询、高级类型(JSON/GIS/向量)、严格一致性PostgreSQL
  • 简单 Web 应用、超大规模读密集、已有 MySQL 生态 → MySQL
  • 2026 年趋势:PostgreSQL 使用率已超过 MySQL,正成为默认选择

十九、学习资源

19.1 官方资源

19.2 中文资源

19.3 经典书籍

  • 📕 《PostgreSQL 技术内幕:事务处理深度探索》--- 张树杰
  • 📗 《PostgreSQL 修炼之道:从小工到专家》--- 唐成
  • 📘 《PostgreSQL Up and Running》--- Regina Obe, Leo Hsu
  • 📙 《The Art of PostgreSQL》--- Dimitri Fontaine
  • 📔 《PostgreSQL: Introduction and Concepts》--- Bruce Momjian

19.4 优质博客与教程

19.5 实用工具与扩展网站

相关推荐
a9511416422 小时前
解决 Bookmarklet 中 %0A 换行符导致的跨环境执行失败问题
jvm·数据库·python
解救女汉子2 小时前
MySQL存储过程运行出错怎么排查_使用DECLARE HANDLER捕获错误
jvm·数据库·python
Absurd5872 小时前
SQL嵌套查询在多租户系统应用_数据隔离逻辑
jvm·数据库·python
2301_782659182 小时前
怎样使用Navicat高级特权进行还原时解决字符集冲突_企业数据保护
jvm·数据库·python
椰猫子2 小时前
数据库(约束、数据库设计(多表关系)、多表查询、事务)
数据库
m0_640309302 小时前
mysql如何处理连接数过多导致响应慢_mysql连接数调优
jvm·数据库·python
weixin_458580122 小时前
PHP怎么实现Toran Proxy代理_PHP依赖包缓存加速【技巧】
jvm·数据库·python
m0_377618232 小时前
Python Selenium怎么定位元素_By.XPATH与By.CSS_SELECTOR操作DOM节点
jvm·数据库·python
2201_761040592 小时前
Layui layer.tips提示框怎么设置方向和颜色
jvm·数据库·python