PostgreSQL 学习教程
从基础到进阶,系统学习 PostgreSQL 数据库的核心知识与实战技巧。
目录
- [一、PostgreSQL 简介](#一、PostgreSQL 简介)
- 二、安装与配置
- 三、数据库与表操作
- 四、数据类型
- 五、查询与过滤
- 六、高级查询
- 七、索引
- 八、函数与存储过程
- 九、视图与物化视图
- 十、事务与并发控制
- [十一、JSON 支持](#十一、JSON 支持)
- 十二、性能优化
- 十三、备份与恢复
- [十四、Java 集成](#十四、Java 集成)
- [十五、实用 SQL 速查](#十五、实用 SQL 速查)
一、PostgreSQL 简介
1.1 什么是 PostgreSQL
PostgreSQL 是一个功能强大的开源对象关系型数据库管理系统(ORDBMS),拥有超过 35 年的活跃开发历史。它以可靠性、健壮性和扩展性著称,被称为"世界上最先进的开源数据库"。
1.2 核心特性
| 特性 | 说明 |
|---|---|
| ACID 事务 | 完整的事务支持,保证数据一致性 |
| MVCC | 多版本并发控制,高并发读写不阻塞 |
| 丰富数据类型 | 原生支持 JSON、数组、UUID、网络地址、几何类型等 |
| 扩展性 | 支持自定义类型、函数、操作符、索引方法 |
| 外键约束 | 完整的参照完整性支持 |
| 递归查询 | 通过 CTE 支持递归 SQL |
| 分区表 | 原生支持范围、列表、哈希分区 |
| 全文搜索 | 内置全文搜索引擎,支持中英文分词 |
| 逻辑复制 | 基于发布/订阅的逻辑复制 |
| 丰富生态 | PostGIS(地理信息)、pgvector(向量检索)、TimescaleDB(时序)等扩展 |
1.3 与 MySQL 对比
| 维度 | PostgreSQL | MySQL |
|---|---|---|
| SQL 标准 | 更严格遵循 | 部分兼容 |
| 并发模型 | MVCC | 表锁 + 行锁混合 |
| 复杂查询 | 强(窗口函数、CTE、递归) | 8.0 后逐步增强 |
| 数据类型 | 非常丰富 | 较基础 |
| 扩展能力 | 强(C 语言扩展、各种 Extension) | 相对有限 |
| JSON 支持 | JSONB 二进制格式,性能好 | 5.7+ 支持,功能相对简单 |
| 适用场景 | 复杂业务、地理信息、数据分析 | 简单 CRUD、高并发读写 |
二、安装与配置
2.1 macOS 安装
bash
# 使用 Homebrew 安装
brew install postgresql@16
# 启动服务
brew services start postgresql@16
# 验证
psql --version
2.2 Linux(Ubuntu/Debian)安装
bash
# 添加官方仓库
sudo sh -c 'echo "deb http://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 -
sudo apt-get update
# 安装
sudo apt-get install postgresql-16
# 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql
2.3 Docker 安装
bash
docker run -d \
--name postgres \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-e POSTGRES_DB=mydb \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:16
2.4 连接数据库
bash
# 本地连接
psql -U postgres
# 指定主机和端口
psql -h localhost -p 5432 -U myuser -d mydb
# 连接字符串方式
psql "postgresql://myuser:mypassword@localhost:5432/mydb"
2.5 核心配置参数
配置文件位置(通常在 /etc/postgresql/16/main/):
postgresql.conf --- 核心参数:
ini
# 监听地址
listen_addresses = 'localhost'
# 端口
port = 5432
# 最大连接数
max_connections = 100
# 共享内存缓冲区(建议为系统内存的 25%)
shared_buffers = 256MB
# 有效缓存大小(建议为系统内存的 50%-75%)
effective_cache_size = 1GB
# WAL 日志相关
wal_level = replica
max_wal_size = 1GB
# 日志
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'ddl'
# 时区
timezone = 'Asia/Shanghai'
pg_hba.conf --- 客户端认证:
ini
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all 0.0.0.0/0 scram-sha-256
三、数据库与表操作
3.1 数据库操作
sql
-- 创建数据库
CREATE DATABASE mydb WITH ENCODING 'UTF8' LC_COLLATE='zh_CN.UTF-8' LC_CTYPE='zh_CN.UTF-8';
-- 查看所有数据库
\l
-- 切换数据库
\c mydb
-- 删除数据库
DROP DATABASE IF EXISTS mydb;
-- 修改数据库所有者
ALTER DATABASE mydb OWNER TO myuser;
3.2 Schema 操作
sql
-- 创建 Schema
CREATE SCHEMA app;
-- 在指定 Schema 下建表
CREATE TABLE app.users (...);
-- 设置搜索路径(优先查找的 Schema)
SET search_path TO app, public;
-- 查看 Schema
\dn
3.3 创建表
sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
email VARCHAR(128) NOT NULL,
password VARCHAR(256) NOT NULL,
nickname VARCHAR(64),
avatar_url VARCHAR(512),
status SMALLINT NOT NULL DEFAULT 1,
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 添加表注释
COMMENT ON TABLE users IS '用户表';
-- 添加列注释
COMMENT ON COLUMN users.status IS '状态:1-正常,0-禁用';
3.4 约束
sql
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
amount NUMERIC(12,2) NOT NULL CHECK (amount > 0),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','paid','shipped','completed','cancelled')),
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 外键约束
CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 外键带级联
ALTER TABLE orders
ADD CONSTRAINT fk_order_user
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- 删除用户时级联删除订单
ON UPDATE SET NULL; -- 更新用户ID时设为NULL
-- 唯一约束(联合唯一)
ALTER TABLE orders ADD CONSTRAINT uk_user_status UNIQUE (user_id, status);
-- 添加/删除列
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;
-- 修改列类型
ALTER TABLE users ALTER COLUMN nickname TYPE VARCHAR(128);
-- 设置默认值
ALTER TABLE users ALTER COLUMN status SET DEFAULT 1;
-- 设置非空
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
-- 重命名表/列
ALTER TABLE users RENAME TO sys_user;
ALTER TABLE users RENAME COLUMN username TO login_name;
3.5 插入数据
sql
-- 单行插入
INSERT INTO users (username, email, password, nickname)
VALUES ('zhangsan', 'zhangsan@example.com', 'hashed_pwd', '张三');
-- 多行插入
INSERT INTO users (username, email, password) VALUES
('lisi', 'lisi@example.com', 'hashed_pwd1'),
('wangwu', 'wangwu@example.com', 'hashed_pwd2'),
('zhaoliu', 'zhaoliu@example.com', 'hashed_pwd3');
-- 插入并返回
INSERT INTO users (username, email, password) VALUES
('qianqi', 'qianqi@example.com', 'hashed_pwd4')
RETURNING id, username, create_time;
-- 从查询插入
INSERT INTO user_backup (username, email, create_time)
SELECT username, email, create_time FROM users WHERE status = 1;
-- 冲突时更新(UPSERT)
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'new@example.com', 'new_pwd')
ON CONFLICT (username)
DO UPDATE SET
email = EXCLUDED.email,
password = EXCLUDED.password,
update_time = NOW();
-- 冲突时忽略
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'test@example.com', 'pwd')
ON CONFLICT (username) DO NOTHING;
3.6 更新与删除
sql
-- 更新
UPDATE users SET nickname = '张三丰', update_time = NOW() WHERE id = 1;
-- 更新并返回
UPDATE users SET status = 0 WHERE id = 1 RETURNING *;
-- 条件删除
DELETE FROM users WHERE id = 1;
-- 删除并返回
DELETE FROM users WHERE status = 0 AND create_time < '2025-01-01' RETURNING id;
3.7 清空表
sql
-- TRUNCATE 比 DELETE FROM 更快(不逐行删除,直接清空表)
TRUNCATE TABLE orders;
-- 带级联清空(清除关联表数据)
TRUNCATE TABLE users CASCADE;
-- 重置自增序列
TRUNCATE TABLE users RESTART IDENTITY;
四、数据类型
4.1 数值类型
| 类型 | 说明 | 范围 |
|---|---|---|
SMALLINT |
2字节整数 | -32768 ~ 32767 |
INTEGER / INT |
4字节整数 | -21亿 ~ 21亿 |
BIGINT |
8字节整数 | 非常大 |
SERIAL |
自增 4字节整数 | 1 ~ 21亿 |
BIGSERIAL |
自增 8字节整数 | 1 ~ 很大 |
NUMERIC(p,s) |
精确小数 | p=精度, s=小数位 |
REAL |
4字节浮点 | 6位精度 |
DOUBLE PRECISION |
8字节浮点 | 15位精度 |
MONEY |
货币类型 | 自动格式化 |
sql
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
price NUMERIC(10,2) NOT NULL, -- 精确到分,推荐用于金额
stock INTEGER NOT NULL DEFAULT 0,
weight REAL -- 浮点,允许精度损失
);
金额字段务必使用
NUMERIC,REAL/DOUBLE存在精度丢失风险。
4.2 字符类型
| 类型 | 说明 |
|---|---|
CHAR(n) |
定长,不足补空格 |
VARCHAR(n) |
变长,有最大长度限制 |
TEXT |
无长度限制的变长文本 |
sql
title VARCHAR(256) -- 标题,限制长度
content TEXT -- 正文,不限长度
4.3 日期时间类型
| 类型 | 说明 | 示例 |
|---|---|---|
TIMESTAMP |
不带时区的时间戳 | 2026-03-31 10:30:00 |
TIMESTAMPTZ |
带时区的时间戳(推荐) | 2026-03-31 10:30:00+08:00 |
DATE |
日期 | 2026-03-31 |
TIME |
时间 | 10:30:00 |
INTERVAL |
时间间隔 | 2 hours, 30 minutes |
sql
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
start_time TIMESTAMPTZ NOT NULL,
duration INTERVAL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 插入
INSERT INTO events (name, start_time, duration) VALUES
('技术分享', '2026-03-31 14:00:00+08:00', INTERVAL '2 hours 30 minutes'),
('部门周会', '2026-04-01 09:30:00+08:00', INTERVAL '1 hour');
-- 日期时间运算
SELECT NOW() + INTERVAL '1 day'; -- 明天
SELECT NOW() - INTERVAL '3 hours'; -- 3小时前
SELECT NOW() + INTERVAL '1 month 2 days'; -- 1个月零2天后
SELECT date_trunc('month', NOW()); -- 当前月初
SELECT EXTRACT(YEAR FROM NOW()); -- 当前年份
SELECT EXTRACT(DOW FROM NOW()); -- 星期几 (0=周日)
4.4 布尔类型
sql
CREATE TABLE articles (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(256),
is_published BOOLEAN NOT NULL DEFAULT FALSE,
is_top BOOLEAN NOT NULL DEFAULT FALSE
);
-- 插入(多种写法)
INSERT INTO articles (title, is_published) VALUES ('测试', TRUE);
INSERT INTO articles (title, is_published) VALUES ('测试', 't');
INSERT INTO articles (title, is_published) VALUES ('测试', 'yes');
INSERT INTO articles (title, is_published) VALUES ('测试', 1); -- 0/1 也可以
-- 查询
SELECT * FROM articles WHERE is_published = TRUE;
SELECT * FROM articles WHERE NOT is_top;
4.5 数组类型
sql
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(256) NOT NULL,
tags TEXT[] -- 文本数组
);
INSERT INTO posts (title, tags) VALUES
('PostgreSQL教程', ARRAY['数据库', 'PostgreSQL', '后端']),
('Java入门', ARRAY['Java', '编程', '后端']);
-- 查询包含特定标签的记录
SELECT * FROM posts WHERE '数据库' = ANY(tags);
-- 追加元素
UPDATE posts SET tags = array_append(tags, '入门') WHERE id = 1;
-- 数组长度
SELECT title, array_length(tags, 1) AS tag_count FROM posts;
-- 展开数组为多行
SELECT unnest(tags) AS tag FROM posts WHERE id = 1;
4.6 UUID 类型
sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE files (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(256) NOT NULL,
size BIGINT NOT NULL
);
INSERT INTO files (name, size) VALUES ('readme.md', 2048);
-- id 自动生成类似: 550e8400-e29b-41d4-a716-446655440000
4.7 网络地址类型
sql
CREATE TABLE access_logs (
id BIGSERIAL PRIMARY KEY,
ip_address INET, -- IPv4 / IPv6
mac_address MACADDR, -- MAC 地址
access_time TIMESTAMPTZ DEFAULT NOW()
);
INSERT INTO access_logs (ip_address, mac_address) VALUES
('192.168.1.100', '08:00:2b:01:02:03'),
('10.0.0.1/24', '08-00-2b-01-02-04');
-- 包含查询
SELECT * FROM access_logs WHERE ip_address << '192.168.1.0/24';
4.8 枚举类型
sql
-- 创建枚举类型
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'completed', 'cancelled');
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
status order_status NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (status) VALUES ('paid');
-- 查看枚举值
SELECT unnest(enum_range(NULL::order_status));
4.9 SERIAL vs IDENTITY(自增主键)
sql
-- 方式1: SERIAL(传统方式,创建序列)
CREATE TABLE t1 (id SERIAL PRIMARY KEY, name TEXT);
-- 方式2: GENERATED ALWAYS AS IDENTITY(SQL 标准,推荐)
CREATE TABLE t2 (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT
);
-- 方式3: GENERATED BY DEFAULT AS IDENTITY(可手动指定值)
CREATE TABLE t3 (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name TEXT
);
五、查询与过滤
5.1 基础查询
sql
SELECT * FROM users;
-- 指定列
SELECT id, username, nickname FROM users;
-- 别名
SELECT id AS "用户ID", username AS "用户名", nickname AS "昵称" FROM users;
-- 去重
SELECT DISTINCT status FROM users;
-- 限制返回行数
SELECT * FROM users LIMIT 10;
-- 分页(PostgreSQL 特有语法,优于 LIMIT OFFSET)
SELECT * FROM users LIMIT 10 OFFSET 20; -- 第3页,每页10条
5.2 条件过滤
sql
-- WHERE 基础
SELECT * FROM users WHERE status = 1;
SELECT * FROM users WHERE age > 18 AND age < 60;
SELECT * FROM users WHERE status IN (1, 2, 3);
SELECT * FROM users WHERE nickname IS NOT NULL;
SELECT * FROM users WHERE username LIKE 'zhang%'; -- 前缀匹配
SELECT * FROM users WHERE username LIKE '%san%'; -- 包含
SELECT * FROM users WHERE username LIKE '___'; -- 精确3个字符
SELECT * FROM users WHERE username ILIKE 'ZHANG%'; -- 不区分大小写
-- 正则匹配
SELECT * FROM users WHERE username ~ '^zhang'; -- 以zhang开头
SELECT * FROM users WHERE username ~* '^ZHANG'; -- 不区分大小写
SELECT * FROM users WHERE username !~ '^test'; -- 不匹配
-- BETWEEN
SELECT * FROM users WHERE create_time BETWEEN '2026-01-01' AND '2026-03-31';
5.3 排序
sql
SELECT * FROM users ORDER BY create_time DESC;
-- 多列排序
SELECT * FROM users ORDER BY status ASC, create_time DESC;
-- NULL 排序(NULLS FIRST / NULLS LAST)
SELECT * FROM users ORDER BY nickname NULLS LAST;
5.4 聚合函数
sql
SELECT
COUNT(*) AS total_count,
COUNT(email) AS email_count, -- 不统计NULL
AVG(age) AS avg_age,
MAX(create_time) AS latest_create,
MIN(create_time) AS earliest_create,
SUM(order_amount) AS total_amount
FROM users;
-- GROUP BY
SELECT status, COUNT(*) AS cnt FROM users GROUP BY status;
-- HAVING(过滤分组后的结果)
SELECT status, COUNT(*) AS cnt
FROM users
GROUP BY status
HAVING COUNT(*) > 10;
5.5 CASE 表达式
sql
-- 简单 CASE
SELECT id, status,
CASE status
WHEN 1 THEN '正常'
WHEN 0 THEN '禁用'
ELSE '未知'
END AS status_text
FROM users;
-- 搜索 CASE
SELECT id, age,
CASE
WHEN age < 18 THEN '未成年'
WHEN age BETWEEN 18 AND 35 THEN '青年'
WHEN age BETWEEN 36 AND 55 THEN '中年'
ELSE '老年'
END AS age_group
FROM users;
六、高级查询
6.1 多表连接
sql
-- 内连接
SELECT u.username, o.id AS order_id, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- 左连接
SELECT u.username, o.id AS order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL; -- 找出没有订单的用户
-- 右连接
SELECT u.username, o.amount
FROM orders o
RIGHT JOIN users u ON o.user_id = u.id;
-- 全外连接
SELECT * FROM table_a FULL OUTER JOIN table_b ON table_a.id = table_b.id;
-- 多表连接
SELECT u.username, o.amount, p.name AS product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.status = 1;
-- 表自连接(查找同一分类下的其他产品)
SELECT p1.name, p2.name AS sibling
FROM products p1
JOIN products p2 ON p1.category_id = p2.category_id AND p1.id != p2.id;
6.2 子查询
sql
-- WHERE 子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
-- FROM 子查询(派生表)
SELECT status_group, cnt
FROM (
SELECT
CASE WHEN age < 18 THEN 'minor' ELSE 'adult' END AS status_group,
COUNT(*) AS cnt
FROM users
GROUP BY status_group
) sub;
-- EXISTS 子查询(比 IN 更高效,短路求值)
SELECT u.username FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- NOT EXISTS
SELECT u.username FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- 关联子查询
SELECT u.username, u.email,
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u;
6.3 CTE(公共表表达式)
sql
-- 基础 CTE
WITH active_users AS (
SELECT * FROM users WHERE status = 1
),
user_orders AS (
SELECT u.username, COUNT(o.id) AS order_count
FROM active_users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.username
)
SELECT * FROM user_orders WHERE order_count > 5;
-- 多个 CTE(用逗号分隔)
WITH
monthly_sales AS (
SELECT date_trunc('month', create_time) AS month, SUM(amount) AS total
FROM orders
GROUP BY month
),
monthly_growth AS (
SELECT month, total,
LAG(total) OVER (ORDER BY month) AS prev_total
FROM monthly_sales
)
SELECT month, total, prev_total,
ROUND((total - prev_total) / prev_total * 100, 2) AS growth_rate
FROM monthly_growth;
6.4 递归查询(CTE RECURSIVE)
sql
-- 组织架构树(经典场景)
-- 表结构:employee(id, name, manager_id)
WITH RECURSIVE org_tree AS (
-- 基础查询:顶级节点(没有上级)
SELECT id, name, manager_id, 1 AS level
FROM employee
WHERE manager_id IS NULL
UNION ALL
-- 递归查询:向下查找下属
SELECT e.id, e.name, e.manager_id, t.level + 1
FROM employee e
INNER JOIN org_tree t ON e.manager_id = t.id
)
SELECT * FROM org_tree ORDER BY level, name;
-- 菜单树
WITH RECURSIVE menu_tree AS (
SELECT id, name, parent_id, name AS path, 0 AS depth
FROM menu WHERE parent_id IS NULL
UNION ALL
SELECT m.id, m.name, m.parent_id, mt.path || ' > ' || m.name, mt.depth + 1
FROM menu m
JOIN menu_tree mt ON m.parent_id = mt.id
)
SELECT * FROM menu_tree ORDER BY path;
6.5 窗口函数
sql
-- ROW_NUMBER:行号(不重复)
SELECT id, username, create_time,
ROW_NUMBER() OVER (ORDER BY create_time DESC) AS row_num
FROM users;
-- PARTITION BY 分组排名
SELECT department, name, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;
-- RANK / DENSE_RANK(有并列)
-- RANK: 1,2,2,4(跳号)
-- DENSE_RANK: 1,2,2,3(不跳号)
SELECT name, salary,
RANK() OVER (ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rnk
FROM employees;
-- LAG / LEAD(前一行/后一行)
SELECT create_time, amount,
LAG(amount) OVER (ORDER BY create_time) AS prev_day,
LEAD(amount) OVER (ORDER BY create_time) AS next_day
FROM daily_sales;
-- 累计求和
SELECT create_time, amount,
SUM(amount) OVER (ORDER BY create_time) AS running_total
FROM daily_sales;
-- 移动平均(最近7天)
SELECT create_time, amount,
AVG(amount) OVER (
ORDER BY create_time
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS ma_7day
FROM daily_sales;
-- FIRST_VALUE / LAST_VALUE
SELECT department, name, salary,
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary DESC) AS top_earner
FROM employees;
6.6 UNION / INTERSECT / EXCEPT
sql
-- UNION(去重合并)
SELECT username FROM users_a
UNION
SELECT username FROM users_b;
-- UNION ALL(不去重,更快)
SELECT username, 'table_a' AS source FROM users_a
UNION ALL
SELECT username, 'table_b' AS source FROM users_b;
-- INTERSECT(交集)
SELECT product_id FROM orders_2025_q1
INTERSECT
SELECT product_id FROM orders_2025_q2;
-- EXCEPT(差集)
SELECT product_id FROM all_products
EXCEPT
SELECT product_id FROM discontinued_products;
七、索引
7.1 索引类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
| B-tree | 默认类型,支持等值、范围、排序 | 大多数查询场景 |
| Hash | 仅支持等值匹配 | 精确查找 |
| GIN | 倒排索引 | JSON、数组、全文搜索 |
| GiST | 通用搜索树 | 地理数据、范围类型 |
| BRIN | 块范围索引 | 时间序列、超大表 |
| SP-GiST | 空间分区 GiST | 特殊数据结构 |
7.2 创建索引
sql
-- 普通索引
CREATE INDEX idx_users_username ON users(username);
-- 唯一索引
CREATE UNIQUE INDEX idx_users_email ON users(email);
-- 复合索引(注意列顺序,最常用列放前面)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
-- 条件索引(部分索引,减少索引体积)
CREATE INDEX idx_active_users ON users(create_time) WHERE status = 1;
-- 表达式索引
CREATE INDEX idx_users_lower_name ON users(LOWER(username));
-- 并发创建(不锁表,生产环境推荐)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
7.3 删除与重建
sql
-- 删除索引
DROP INDEX idx_users_username;
-- 并发删除
DROP INDEX CONCURRENTLY idx_users_username;
-- 重建索引
REINDEX INDEX idx_users_username;
-- 并发重建
REINDEX INDEX CONCURRENTLY idx_users_username;
7.4 查看索引使用情况
sql
-- 查看表的索引
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';
-- 查看索引大小
SELECT pg_size_pretty(pg_relation_size('idx_users_username'));
-- 查看索引使用统计(判断索引是否有效)
SELECT schemaname, relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
7.5 EXPLAIN 分析查询计划
sql
EXPLAIN SELECT * FROM users WHERE username = 'zhangsan';
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
关键字段解读:
Seq Scan--- 全表扫描(通常需要优化)Index Scan--- 索引扫描Bitmap Index Scan--- 位图索引扫描(适合批量查询)Hash Join--- 哈希连接Nested Loop--- 嵌套循环连接cost=.. rows=..--- 预估成本和行数actual time=.. rows=..--- 实际执行时间和行数
八、函数与存储过程
8.1 内置常用函数
sql
-- 字符串
SELECT LENGTH('hello'), LEFT('hello', 3), RIGHT('hello', 2);
SELECT UPPER('hello'), LOWER('HELLO'), INITCAP('hello world');
SELECT TRIM(' hello '), REPLACE('abc', 'b', 'x');
SELECT SUBSTRING('hello world', 7, 5); -- world
SELECT SPLIT_PART('a,b,c,d', ',', 2); -- b
SELECT STRING_AGG(name, ', ') FROM users; -- 拼接
-- 数学
SELECT ROUND(3.14159, 2), CEIL(3.14), FLOOR(3.99);
SELECT ABS(-5), MOD(10, 3), POWER(2, 10);
-- 日期
SELECT NOW(), CURRENT_DATE, CURRENT_TIME;
SELECT AGE(NOW(), '2026-01-01');
SELECT DATE_PART('year', NOW());
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_TIMESTAMP('2026-03-31 14:30:00', 'YYYY-MM-DD HH24:MI:SS');
-- NULL 处理
SELECT COALESCE(NULL, NULL, 'default'); -- default
SELECT NULLIF(10, 10); -- NULL(相等返回NULL)
SELECT NULLIF(10, 20); -- 10(不等返回原值)
8.2 自定义函数
sql
-- PL/pgSQL 函数
CREATE OR REPLACE FUNCTION get_user_order_count(p_user_id BIGINT)
RETURNS INTEGER AS $$
DECLARE
v_count INTEGER;
BEGIN
SELECT COUNT(*) INTO v_count FROM orders WHERE user_id = p_user_id;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT get_user_order_count(1);
-- 返回表的函数
CREATE OR REPLACE FUNCTION get_top_users(p_limit INT)
RETURNS TABLE(id BIGINT, username VARCHAR, order_count BIGINT) AS $$
BEGIN
RETURN QUERY
SELECT u.id, u.username, COUNT(o.id)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY order_count DESC
LIMIT p_limit;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_top_users(10);
8.3 存储过程
sql
-- 存储过程(支持事务控制)
CREATE OR REPLACE PROCEDURE batch_update_user_status(
p_status SMALLINT,
p_min_orders INT
)
LANGUAGE plpgsql
AS $$
BEGIN
-- 更新订单数超过阈值的用户状态
UPDATE users SET status = p_status, update_time = NOW()
WHERE id IN (
SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(*) >= p_min_orders
);
-- 记录日志
RAISE NOTICE '批量更新完成,影响行数: %', ROW_COUNT;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE EXCEPTION '批量更新失败: %', SQLERRM;
END;
$$;
-- 调用存储过程
CALL batch_update_user_status(1, 10);
8.4 触发器
sql
-- 创建触发器函数
CREATE OR REPLACE FUNCTION update_user_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_time = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器
CREATE TRIGGER trg_user_update_time
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_user_timestamp();
-- 测试:更新任意字段,update_time 会自动更新
UPDATE users SET nickname = '新昵称' WHERE id = 1;
-- 带条件的触发器
CREATE OR REPLACE FUNCTION log_order_status_change()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.status IS DISTINCT FROM NEW.status THEN
INSERT INTO order_status_log(order_id, old_status, new_status, change_time)
VALUES (NEW.id, OLD.status, NEW.status, NOW());
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_order_status_log
AFTER UPDATE ON orders
FOR EACH ROW
EXECUTE FUNCTION log_order_status_change();
九、视图与物化视图
9.1 普通视图
sql
-- 创建视图
CREATE VIEW v_user_order_summary AS
SELECT
u.id AS user_id,
u.username,
u.nickname,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.amount), 0) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username, u.nickname;
-- 查询视图(与普通表一致)
SELECT * FROM v_user_order_summary WHERE total_amount > 1000;
-- 可更新视图(简单视图可直接更新)
CREATE VIEW v_simple_users AS
SELECT id, username, nickname FROM users WHERE status = 1;
UPDATE v_simple_users SET nickname = '新昵称' WHERE id = 1; -- 实际更新 users 表
9.2 物化视图(数据快照,提升查询性能)
sql
-- 创建物化视图
CREATE MATERIALIZED VIEW mv_order_daily_stats AS
SELECT
DATE(create_time) AS order_date,
COUNT(*) AS order_count,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount
FROM orders
GROUP BY DATE(create_time);
-- 手动刷新(全量刷新)
REFRESH MATERIALIZED VIEW mv_order_daily_stats;
-- 并发刷新(不阻塞查询)
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_order_daily_stats;
-- 带数据的物化视图
CREATE MATERIALIZED VIEW mv_user_stats WITH DATA AS
SELECT user_id, COUNT(*), SUM(amount)
FROM orders GROUP BY user_id;
-- 不立即填充数据
CREATE MATERIALIZED VIEW mv_user_stats WITH NO DATA AS
SELECT user_id, COUNT(*), SUM(amount)
FROM orders GROUP BY user_id;
-- 删除
DROP MATERIALIZED VIEW mv_order_daily_stats;
普通视图 vs 物化视图:普通视图每次查询都执行底层 SQL,数据实时;物化视图将结果存为物理数据,查询快但需要手动刷新。
十、事务与并发控制
10.1 事务基础
sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 或回滚
ROLLBACK;
10.2 事务隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|---|---|---|---|
| Read Uncommitted | 可能 | 可能 | 可能 | 几乎不用 |
| Read Committed | 不可能 | 可能 | 可能 | PostgreSQL 默认 |
| Repeatable Read | 不可能 | 不可能 | 可能 | 推荐大多数场景 |
| Serializable | 不可能 | 不可能 | 不可能 | 最高隔离,性能最低 |
sql
-- 查看当前隔离级别
SHOW transaction_isolation;
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 会话级别设置
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;
10.3 锁机制
sql
-- 显式加锁
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 行级排他锁
SELECT * FROM users WHERE id = 1 FOR NO KEY UPDATE; -- 不锁外键
SELECT * FROM users WHERE id = 1 FOR SHARE; -- 行级共享锁
-- 表级锁
LOCK TABLE users IN ACCESS EXCLUSIVE MODE;
-- 查看当前锁
SELECT pid, relation, mode, granted
FROM pg_locks
WHERE relation = 'users'::regclass;
-- 查看阻塞
SELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid
FROM pg_locks blocked
JOIN pg_locks blocking ON blocked.locktype = blocking.locktype
AND blocked.database IS NOT DISTINCT FROM blocking.database
AND blocked.relation IS NOT DISTINCT FROM blocking.relation
AND blocked.page IS NOT DISTINCT FROM blocking.page
AND blocked.tuple IS NOT DISTINCT FROM blocking.tuple
AND blocked.pid != blocking.pid
WHERE NOT blocked.granted;
10.4 乐观锁(版本号方案)
sql
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
stock INTEGER NOT NULL DEFAULT 0,
version INTEGER NOT NULL DEFAULT 0 -- 版本号
);
-- 更新时检查版本号
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 5;
-- 如果 affected rows = 0,说明版本号已变,需要重试
10.5 Advisory Lock(应用级锁)
sql
-- 获取会话级排他锁(会话结束自动释放)
SELECT pg_advisory_lock(12345);
-- 尝试获取(不阻塞,返回是否成功)
SELECT pg_try_advisory_lock(12345);
-- 释放
SELECT pg_advisory_unlock(12345);
-- 事务级锁(事务结束自动释放)
SELECT pg_advisory_xact_lock(12345);
Java 中应用 :对于需要分布式锁的场景,可以用
pg_try_advisory_lock实现基于数据库的分布式锁。
十一、JSON 支持
11.1 JSON vs JSONB
| 类型 | 存储格式 | 查询性能 | 写入性能 | 适用场景 |
|---|---|---|---|---|
JSON |
原始文本 | 较慢 | 较快 | 需要保留原始格式 |
JSONB |
二进制 | 快(支持索引) | 稍慢(需转换) | 推荐,大多数场景 |
11.2 JSONB 基本操作
sql
CREATE TABLE configs (
id BIGSERIAL PRIMARY KEY,
key VARCHAR(64) NOT NULL UNIQUE,
value JSONB NOT NULL
);
-- 插入
INSERT INTO configs (key, value) VALUES
('site_name', '"我的网站"'),
('user_settings', '{"theme":"dark","lang":"zh","fontSize":14}'),
('feature_flags', '{"new_ui":true,"beta":false,"admin_features":["a","b","c"]}');
-- 查询(-> 返回 JSON,->> 返回文本)
SELECT value->>'theme' FROM configs WHERE key = 'user_settings'; -- dark
SELECT value->'admin_features' FROM configs WHERE key = 'feature_flags'; -- ["a","b","c"]
SELECT value->'admin_features'->>0 FROM configs WHERE key = 'feature_flags'; -- a
-- 路径查询
SELECT value #>> '{user_settings,theme}' FROM configs WHERE key = 'user_settings';
-- 条件过滤
SELECT * FROM configs WHERE value->>'theme' = 'dark';
SELECT * FROM configs WHERE value @> '{"new_ui":true}'; -- 包含
SELECT * FROM configs WHERE value ? 'admin_features'; -- 键存在
SELECT * FROM configs WHERE value ?| array['theme','lang']; -- 任一键存在
SELECT * FROM configs WHERE value ?& array['theme','lang']; -- 所有键存在
11.3 JSONB 修改
sql
-- 更新整个 JSON 值
UPDATE configs SET value = '{"theme":"light","lang":"en"}' WHERE key = 'user_settings';
-- 修改某个键值
UPDATE configs SET value = jsonb_set(value, '{theme}', '"light"') WHERE key = 'user_settings';
-- 新增键
UPDATE configs SET value = value || '{"timezone":"Asia/Shanghai"}' WHERE key = 'user_settings';
-- 删除键
UPDATE configs SET value = value - 'fontSize' WHERE key = 'user_settings';
11.4 JSONB 索引
sql
-- GIN 索引(支持 @>, ?, ?|, ?& 操作符)
CREATE INDEX idx_configs_value ON configs USING GIN (value);
-- JSONPath 索引(PG 12+)
CREATE INDEX idx_configs_value_path ON configs USING GIN (value jsonb_path_ops);
11.5 将查询结果转 JSON
sql
-- 单行转 JSON 对象
SELECT row_to_json(u) FROM users u WHERE id = 1;
-- 多行转 JSON 数组
SELECT json_agg(row_to_json(u)) FROM users u WHERE status = 1;
-- 更精细的控制
SELECT json_build_object(
'user_id', u.id,
'username', u.username,
'order_count', COALESCE(o.cnt, 0)
)
FROM users u
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) o ON u.id = o.user_id;
十二、性能优化
12.1 查询优化
sql
-- 1. 使用 EXPLAIN ANALYZE 分析慢查询
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT * FROM users WHERE email = 'test@example.com';
-- 2. 避免 SELECT *
-- 只查需要的列,减少 IO 和内存
-- 3. 避免在 WHERE 子句中对列使用函数(会导致索引失效)
-- 错误写法
SELECT * FROM users WHERE LOWER(username) = 'zhangsan';
-- 正确写法:创建表达式索引
CREATE INDEX idx_users_lower ON users(LOWER(username));
-- 4. LIKE 前缀匹配才走索引
SELECT * FROM users WHERE username LIKE 'zhang%'; -- 走索引
SELECT * FROM users WHERE username LIKE '%zhang%'; -- 不走索引,考虑全文搜索或 pg_trgm
-- 5. 使用 LIMIT 限制结果集
SELECT * FROM orders ORDER BY create_time DESC LIMIT 100;
-- 6. 批量操作优于单条操作
-- 用 INSERT ... VALUES (...), (...), (...) 代替多次 INSERT
12.2 统计信息收集
sql
-- 手动收集统计信息(大表变更后建议执行)
ANALYZE users;
-- 收集并显示详细信息
ANALYZE VERBOSE users;
-- 查看表的统计信息
SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_autovacuum, last_analyze
FROM pg_stat_user_tables
WHERE relname = 'users';
12.3 VACUUM
sql
-- 回收死元组空间(日常维护)
VACUUM users;
-- 回收空间并更新统计信息
VACUUM ANALYZE users;
-- 回收空间并允许操作系统回收磁盘空间
VACUUM FULL users; -- 注意:会锁表,生产环境使用 pg_repack 代替
-- 自动清理配置(postgresql.conf)
# autovacuum = on -- 默认开启
# autovacuum_vacuum_scale_factor = 0.2 -- 20%行变更后触发
# autovacuum_analyze_scale_factor = 0.1
12.4 连接池
PostgreSQL 原生不支持连接池,推荐使用以下方案:
pgBouncer(推荐):
ini
; pgbouncer.ini
[databases]
mydb = host=localhost port=5432 dbname=mydb
[pgbouncer]
pool_mode = transaction -- 事务级池化
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
listen_port = 6432
12.5 分区表
sql
-- 创建分区主表
CREATE TABLE orders (
id BIGSERIAL,
user_id BIGINT NOT NULL,
amount NUMERIC(12,2) NOT NULL,
status VARCHAR(20) NOT NULL,
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (create_time);
-- 创建分区(按月)
CREATE TABLE orders_2026_01 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE orders_2026_03 PARTITION OF orders
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
-- 默认分区(兜底)
CREATE TABLE orders_default PARTITION OF orders DEFAULT;
-- 查询自动路由到对应分区
SELECT * FROM orders WHERE create_time >= '2026-03-01' AND create_time < '2026-04-01';
-- 创建索引(每个分区自动创建)
CREATE INDEX idx_orders_user_id ON orders(user_id);
十三、备份与恢复
13.1 逻辑备份(pg_dump)
bash
# 备份单个数据库(SQL格式)
pg_dump -U postgres -d mydb -f mydb_backup.sql
# 备份(自定义格式,支持并行恢复)
pg_dump -U postgres -d mydb -Fc -f mydb_backup.dump
# 备份(目录格式,支持并行备份)
pg_dump -U postgres -d mydb -Fd -j 4 -f mydb_backup_dir/
# 仅备份表结构
pg_dump -U postgres -d mydb --schema-only -f schema.sql
# 仅备份数据
pg_dump -U postgres -d mydb --data-only -f data.sql
# 备份指定表
pg_dump -U postgres -d mydb -t users -t orders -f tables.sql
# 备份所有数据库
pg_dumpall -U postgres -f all_databases.sql
13.2 恢复
bash
# 从 SQL 文件恢复
psql -U postgres -d mydb -f mydb_backup.sql
# 从自定义格式恢复(可并行)
pg_restore -U postgres -d mydb -j 4 mydb_backup.dump
# 恢复到新数据库
createdb -U postgres newdb
pg_restore -U postgres -d newdb mydb_backup.dump
13.3 物理备份(pg_basebackup)
bash
# 全量物理备份
pg_basebackup -h localhost -U postgres -D /data/backup/base -Ft -z -P
# 参数说明
# -D 备份目录
# -Ft tar 格式
# -z 压缩
# -P 显示进度
13.4 定时备份脚本
bash
#!/bin/bash
# backup_pg.sh
BACKUP_DIR="/data/backups/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="mydb"
KEEP_DAYS=7
mkdir -p $BACKUP_DIR
# 备份
pg_dump -U postgres -d $DB_NAME -Fc -f "$BACKUP_DIR/${DB_NAME}_${DATE}.dump"
# 清理过期备份
find $BACKUP_DIR -name "${DB_NAME}_*.dump" -mtime +$KEEP_DAYS -delete
echo "备份完成: ${DB_NAME}_${DATE}.dump"
十四、Java 集成
14.1 JDBC 基础
Maven 依赖:
xml
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
JDBC URL 格式:
jdbc:postgresql://localhost:5432/mydb
jdbc:postgresql://localhost:5432/mydb?user=myuser&password=mypassword
jdbc:postgresql://localhost:5432/mydb?sslmode=require
14.2 Spring Boot 集成
yaml
# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: myuser
password: mypassword
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
show-sql: false
open-in-view: false
14.3 MyBatis 集成
yaml
# application.yml
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
jdbc-type-for-null: NULL
default-fetch-size: 100
MyBatis 中使用 PostgreSQL 特性:
xml
<!-- 批量插入(ON CONFLICT 处理冲突) -->
<insert id="batchInsert" parameterType="list">
INSERT INTO users (username, email, password, create_time)
VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.email}, #{user.password}, NOW())
</foreach>
ON CONFLICT (username) DO NOTHING
</insert>
<!-- JSONB 查询 -->
<select id="findByJsonField" resultType="User">
SELECT * FROM users
WHERE settings @> #{filterJson}::jsonb
</select>
<!-- 窗口函数 -->
<select id="getUserRank" resultType="UserRank">
SELECT id, username, score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rank
FROM users
</select>
14.4 连接池配置(HikariCP)
yaml
spring:
datasource:
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 300000
max-lifetime: 1800000
connection-timeout: 30000
pool-name: MyPgPool
十五、实用 SQL 速查
15.1 数据库运维
sql
-- 查看所有数据库及大小
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database ORDER BY pg_database_size(datname) DESC;
-- 查看当前连接数
SELECT count(*) FROM pg_stat_activity;
-- 查看活跃连接
SELECT pid, usename, datname, client_addr, state, query
FROM pg_stat_activity WHERE state = 'active';
-- 查看每个表的行数和大小
SELECT relname AS table_name,
n_live_tup AS row_count,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC;
-- 终止连接
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid = 12345;
-- 查看数据库版本
SELECT version();
15.2 表结构查询
sql
-- 查看表结构
\d users
DESCRIBE users;
-- 查看所有列信息
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'users';
-- 查看索引
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';
-- 查看约束
SELECT conname, contype, pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'users'::regclass;
-- 查看表大小(含索引)
SELECT pg_size_pretty(pg_total_relation_size('users'));
-- 查看表大小(不含索引)
SELECT pg_size_pretty(pg_relation_size('users'));
-- 查看索引大小
SELECT pg_size_pretty(pg_indexes_size('users'));
15.3 常用运维操作
sql
-- 创建只读用户
CREATE ROLE readonly_user WITH LOGIN PASSWORD 'readonly_pass';
GRANT CONNECT ON DATABASE mydb TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
-- 对未来新建的表也生效
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user;
-- 重置序列(表清空后重置自增ID)
SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users), false);
-- 查看长事务
SELECT pid, now() - xact_start AS duration, query, state
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'active')
AND now() - xact_start > interval '5 minutes';
-- 查看表膨胀(需要 pgstattuple 扩展)
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT * FROM pgstattuple('users');
15.4 常用扩展
sql
-- UUID 生成
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 模糊匹配扩展(提升 LIKE '%xxx%' 性能)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- 表统计信息
CREATE EXTENSION IF NOT EXISTS pgstattuple;
-- 全文搜索中文分词(需额外安装 zhparser)
CREATE EXTENSION IF NOT EXISTS zhparser;
-- 地理信息
CREATE EXTENSION IF NOT EXISTS postgis;
-- 向量检索(AI 向量搜索)
CREATE EXTENSION IF NOT EXISTS vector;
-- 超级用户才能创建扩展,普通用户需授权
GRANT ALL ON SCHEMA public TO myuser;