🐘 PostgreSQL 完整知识总结与使用教程
📖 目录
- [一、PostgreSQL 概述](#一、PostgreSQL 概述)
- 二、发展历史与现状
- 三、核心特性
- [四、PostgreSQL 架构深入解析](#四、PostgreSQL 架构深入解析)
- 五、安装与部署
- [六、基础 SQL 操作](#六、基础 SQL 操作)
- 七、数据类型全解
- 八、索引技术详解
- [九、MVCC 与事务隔离](#九、MVCC 与事务隔离)
- [十、JSON/JSONB 全面使用](#十、JSON/JSONB 全面使用)
- 十一、高级特性
- 十二、性能调优
- 十三、备份与恢复
- 十四、复制与高可用
- 十五、扩展生态
- [十六、PostgreSQL 18 新特性](#十六、PostgreSQL 18 新特性)
- 十七、最佳实践
- [十八、与 MySQL 对比](#十八、与 MySQL 对比)
- 十九、学习资源
一、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 官方下载页 下载安装程序:
- 双击运行
.exe安装包 - 选择安装路径(如
C:\Program Files\PostgreSQL\18) - 选择组件:PostgreSQL Server、pgAdmin 4、命令行工具
- 设置数据目录(默认
C:\Program Files\PostgreSQL\18\data) - 设置 postgres 超级用户密码(请牢记!)
- 设置监听端口(默认 5432)
- 完成安装,可选安装 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,绝不要用FLOAT或REAL,因为浮点运算会有精度丢失!
7.2 字符类型
| 类型 | 说明 |
|---|---|
CHAR(n) |
定长,不足补空格(不推荐) |
VARCHAR(n) |
变长,最大 n 字符 |
TEXT |
变长,无长度限制(推荐) |
💡 PostgreSQL 中
TEXT、VARCHAR、VARCHAR(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 不会直接修改原行,而是:
- 标记原行的
xmax= 当前事务 ID(逻辑删除) - 插入一行新版本 ,其
xmin= 当前事务 ID - 其他事务根据自己的快照,可能看到新版本或旧版本
这就是 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/Python、PL/Perl、PL/Tcl、PL/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 官方资源
- 🌐 官网 :https://www.postgresql.org
- 📚 官方文档 :https://www.postgresql.org/docs/
- 📰 发行说明 :https://www.postgresql.org/docs/release/
- 💬 社区 :https://www.postgresql.org/community/
19.2 中文资源
- 📖 PostgreSQL 中文社区 :https://www.postgresqlchina.com
- 📗 PG 中文文档 :http://www.postgres.cn/docs/
- 🌐 Postgres 中国用户组 :
postgres-cn - 📚 菜鸟教程 PostgreSQL :https://www.runoob.com/postgresql/
- 📘 W3Cschool PostgreSQL :https://www.w3cschool.cn/postgresql/
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 优质博客与教程
- 🔗 CrunchyData :https://www.crunchydata.com/blog
- 🔗 TigerData (Timescale) :https://www.tigerdata.com/learn
- 🔗 Citus Data :https://www.citusdata.com/blog/
- 🔗 Postgres Professional :https://postgrespro.com/blog
- 🔗 pganalyze :https://pganalyze.com/blog
- 🔗 EDB Blog :https://www.enterprisedb.com/blog
19.5 实用工具与扩展网站
- 🛠️ PgTune :https://pgtune.leopard.in.ua/ --- 参数调优计算器
- 🛠️ PGXN :https://pgxn.org/ --- PostgreSQL 扩展网络
- 🛠️ pgMustard :https://www.pgmustard.com/ --- 执行计划分析
- 🛠️ explain.depesz.com :https://explain.depesz.com/ --- 执行计划可视化
- 🛠️ DBeaver :https://dbeaver.io/ --- 免费 GUI
- 🛠️ pgAdmin :https://www.pgadmin.org/ --- 官方 GUI