第一章:认知篇 ------ 为什么选择 PostgreSQL?
-
数据库界的"瑞士军刀":全能型(关系型+JSONB+GIS+全文检索),性能媲美 MongoDB,内置搜索可替代部分 ES。
-
极致的严谨性:完美支持 ACID 事务,强类型约束,底层规避 Bug。
-
开源与生态:类 BSD 许可,完全免费。Supabase、Neon 等均以其为底层。
-
职业护城河:大厂标配(Apple、Instagram),支持窗口函数、CTE、逻辑复制。
第二章:结构篇 ------ 逻辑分层与基础操作
1. 核心层级结构(行政大楼比喻)
实例 (Instance) > 数据库 (Database) > 模式 (Schema) > 表 (Table) > 行与列 (Row & Column)
-
数据库 :物理隔离,跨库查询需用
dblink或FDW。 -
模式 (Schema) :PG 的灵魂 。默认是
public,建议多用户系统使用独立 Schema 实现逻辑隔离。
2. 库与模式操作代码
sql
-- 创建数据库
CREATE DATABASE db_name;
-- 查看数据库
\l 或者 SELECT datname FROM pg_database;
-- 接入数据库
\c db_name;
-- 删除数据库 (需先切换到其他库,且不能有会话连接)
DROP DATABASE db_name;
-- 强制删除
DROP DATABASE db WITH (FORCE);
-- 创建模式
CREATE SCHEMA IF NOT EXISTS storage;
-- 查看模式
\dn
-- 删除模式 (只有模式为空时才能删除)
DROP SCHEMA IF EXISTS storage;
-- 【慎用】级联删除:删除模式以及模式里的所有表、视图、函数等
DROP SCHEMA IF EXISTS storage CASCADE;
3. 表基础操作代码
sql
-- 创建表 (带有约束)
CREATE TABLE IF NOT EXISTS storage.files (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- 自增主键
file_id UUID DEFAULT gen_random_uuid(), -- uuid
file_name TEXT NOT NULL, -- 不能为空
file_size BIGINT CHECK (file_size >= 0), -- 检查约束
is_public BOOLEAN DEFAULT false, -- 默认值
tags TEXT[], -- 数组类型
created_at TIMESTAMPTZ DEFAULT NOW() -- 带时区的时间
);
-- 修改表 (ALTER TABLE)
ALTER TABLE storage.files ADD COLUMN download_count INT DEFAULT 0; -- 添加字段
ALTER TABLE storage.files ALTER COLUMN file_name TYPE VARCHAR(255); -- 修改字段类型
ALTER TABLE storage.files RENAME COLUMN is_public TO is_shared; -- 重命名
ALTER TABLE storage.files DROP COLUMN tags; -- 删除字段
-- 删除表与清空表
DROP TABLE IF EXISTS storage.files;
DROP TABLE storage.files CASCADE; -- 级联删除
TRUNCATE TABLE storage.files; -- 清空表数据,保留结构
TRUNCATE TABLE storage.files RESTART IDENTITY; -- 清空并重置自增id
-- 查看表信息
SELECT schemaname AS 模式名, tablename AS 表名, tableowner AS 所有人
FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY schemaname, tablename;
SELECT column_name AS 字段名, data_type AS 数据类型, is_nullable AS 是否允许为空, column_default AS 默认值, character_maximum_length AS 最大长度
FROM information_schema.columns WHERE table_name = 'cloud_files' ORDER BY ordinal_position;
第三章:数据建模篇 ------ 类型、约束与表关系
1. 数据类型
-
核心 :
INT/BIGINT,NUMERIC(p,s)(金额),VARCHAR/TEXT,TIMESTAMPZ(带时区)。 -
特色 :
BOOLEAN,JSONB(支持索引,性能极佳),UUID,ARRAY(如TEXT[]),INET(IP 类型),tsrange(时间范围)。
2. 表关系与约束实战代码
- 一对一 (1:1) :子表外键设为
PRIMARY KEY或UNIQUE。
sql
-- 主表:核心账户
CREATE TABLE users (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
username TEXT NOT NULL UNIQUE
);
-- 扩展表:详细资料
CREATE TABLE user_profiles (
user_id BIGINT PRIMARY KEY, -- 既是主键,也是外键
avatar_url TEXT,
bio TEXT,
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
- 一对多 (1:N):在"多"的一方加外键。
sql
-- 1. 女神表 (一)
CREATE TABLE goddesses (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL,
star_sign TEXT
);
-- 2. 舔狗表 (多)
CREATE TABLE simps (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
target_id BIGINT NOT NULL, -- 目标女神的 ID (外键)
nickname TEXT NOT NULL,
contributed_amount DECIMAL(10, 2) DEFAULT 0,
CONSTRAINT fk_goddess FOREIGN KEY (target_id) REFERENCES goddesses(id) ON DELETE CASCADE
);
-- 查询连表:查看每位舔狗正在追谁
SELECT s.nickname AS 舔狗, g.name AS 女神, s.contributed_amount AS 贡献值
FROM simps s JOIN goddesses g ON s.target_id = g.id ORDER BY s.contributed_amount DESC;
- 多对多 (M:N):引入中间表,联合主键。
sql
-- 1. 学生表
CREATE TABLE students (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL
);
-- 2. 社团表
CREATE TABLE clubs (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
club_name TEXT NOT NULL UNIQUE,
description TEXT
);
-- 3. 成员关系表 (中间桥梁)
CREATE TABLE memberships (
student_id BIGINT REFERENCES students(id) ON DELETE CASCADE,
club_id BIGINT REFERENCES clubs(id) ON DELETE CASCADE,
joined_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (student_id, club_id) -- 联合主键:保证不会加两次
);
-- 多对多查询:想看 “Go 语言社” 里都有哪些大神?
SELECT c.club_name AS 社团名, s.name AS 成员名, m.joined_at AS 入社时间
FROM clubs c JOIN memberships m ON c.id = m.club_id JOIN students s ON m.student_id = s.id
WHERE c.club_name = 'Go语言社';
- 表继承 (Inheritance):PG 黑科技。
sql
-- 父表:定义基础字段
CREATE TABLE content (title TEXT, author TEXT);
-- 子表:继承基础字段,并增加自己的字段
CREATE TABLE video (duration INT) INHERITS (content);
-- 查询 content 表时,它会自动把 video 表里的数据也查出来。
3. 数据 CRUD 与高级查询代码
sql
-- 插入数据 (RETURNING 返回新生成的 UUID)
INSERT INTO files (file_name, file_size, is_public, tags)
VALUES ('课程大纲.pdf', 1024576, true, ARRAY['教育', 'PDF']);
INSERT INTO files (file_name, file_size) VALUES ('枫枫老师的私房课.zip', 999999) RETURNING file_id, created_at;
-- 更新数据
UPDATE files SET download_count = download_count + 1 WHERE file_name = 'demo.mp4';
UPDATE files SET is_public = false, tags = array_append(tags, '私有') WHERE file_id = '某个UUID';
-- 删除数据
DELETE FROM files WHERE file_id = '某个UUID';
DELETE FROM files WHERE download_count = 0 AND tags @> ARRAY['测试'::text];
-- 排序和分页
SELECT file_name, created_at FROM file_details ORDER BY created_at DESC LIMIT 2 OFFSET 0;
-- 聚合统计 (展开数组并计数)
SELECT uploader_ip, COUNT(*) AS upload_count, SUM(array_length(tags, 1)) AS total_tags
FROM file_details GROUP BY uploader_ip;
-- 数据转换与格式化
SELECT left(detail_id::text, 8) AS short_id, file_name, to_char(created_at, 'YYYY-MM-DD') AS simple_date FROM file_details;
-- JSONB 操作
SELECT file_name, metadata->>'author' AS author FROM file_details WHERE metadata->>'author' = 'Fengfeng'; -- 提取text
SELECT file_name, metadata->'video' AS author FROM file_details WHERE (metadata->'video'->'resolution'->>'width')::int = 1920; -- 提取json对象
SELECT * FROM file_details WHERE metadata ? 'color'; -- 键值对存在性判断
-- INET 网络地址筛选
SELECT * FROM file_details WHERE uploader_ip = '127.0.0.1'; -- 精确匹配
SELECT file_name, uploader_ip FROM file_details WHERE uploader_ip << '172.16.0.0/16'; -- 范围匹配
第四章:高阶提效篇 ------ 视图与 CTE
1. 视图 (View):不存数据只存查询语句,常用于权限控制。
sql
-- 创建视图:一次封装,永久受益
CREATE VIEW v_file_detail_full AS
SELECT f.id, f.file_name, u.username AS owner_name, n.node_name AS storage_location, f.created_at
FROM cloud_files f
JOIN users u ON f.user_id = u.id
JOIN storage_nodes n ON f.node_id = n.id;
-- 以后查询只需:
SELECT * FROM v_file_detail_full;
2. CTE (公用表表达式) :使用 WITH 将复杂嵌套 SQL 抽离成变量,提升可读性。
第五章:性能引擎篇 ------ 索引与事务锁
1. 索引代码与原理
-
原理:B-Tree 平衡多路查找树,将全表扫描 O(n)O(n) 降至 O(logmn)O(logmn)。
-
创建不同类型的索引:
sql
-- 显式指定或默认不写都是 B-Tree
CREATE INDEX idx_file_name ON cloud_files USING btree (file_name);
-- 仅适用于 = 查询
CREATE INDEX idx_file_hash ON cloud_files USING hash (file_name);
-- 复合索引:(user_id, file_type) 遵循最左匹配原则
CREATE INDEX idx_user_file_type ON cloud_files (user_id, file_type);
-- GIN 索引:标签查询或 JSONB 格式元数据的“神器”
CREATE INDEX idx_files_tags ON cloud_files USING gin (tags);
CREATE INDEX idx_files_meta ON cloud_files USING gin (meta_data jsonb_path_ops);
-- 唯一索引
CREATE UNIQUE INDEX idx_file_md5 ON cloud_files (file_md5);
-- 分析查询性能
EXPLAIN ANALYZE SELECT * FROM cloud_files WHERE file_name = 'file_999999.pdf';
- 索引失效场景 :
LIKE '%xxx'(左模糊)、函数操作(如UPPER(name))、类型隐式转换(数字查字符串)、不符合复合索引最左匹配。
2. 事务与锁代码
- 事务 ACID:原子性、一致性、隔离性、持久性。
sql
BEGIN; -- 开启事务,立下“生死状”
-- 1. 扣钱
UPDATE accounts SET balance = balance - 100 WHERE username = '小红';
-- 2. 增加会员时长
UPDATE members SET expire_date = expire_date + INTERVAL '1 year' WHERE username = '小红';
-- 检查一下:如果钱扣对了,会员也加了
COMMIT; -- 确认无误,正式写入硬盘
-- 如果中途出错:
ROLLBACK; -- 刚才的所有操作全部撤销,像没发生过一样
- 锁机制(三大层次):
sql
-- 手动锁定表 (大面积维护时使用,不希望别人干扰)
BEGIN;
LOCK TABLE my_table IN ACCESS EXCLUSIVE MODE; -- 彻底锁死表
-- 执行操作...
COMMIT;
-- 使用建议锁 (Advisory Locks):程序员自定义的逻辑锁,不锁表/行
SELECT pg_advisory_lock(12345); -- 获取编号为 12345 的逻辑锁
-- 执行你的业务逻辑...
SELECT pg_advisory_unlock(12345); -- 释放锁
-- 如何查看当前的锁? (排查数据库“卡住了”)
SELECT * FROM pg_locks l JOIN pg_stat_activity a ON l.pid = a.pid;
第六章:杀手锏扩展篇 ------ 一库多用
1. 全文搜索 (替代 Elasticsearch)
sql
-- 查看并安装扩展
SELECT * FROM pg_available_extensions;
SELECT * FROM pg_extension;
-- 英文全文检索示例 (动态分词)
SELECT title, content FROM posts
WHERE to_tsvector('english', title || ' ' || content) @@ to_tsquery('english', 'PostgreSQL | Guide');
-- 性能优化:预分词 (建立 STORED 列并加 GIN 索引)
CREATE TABLE posts1 (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
searchable_index_col tsvector GENERATED ALWAYS AS (to_tsvector('english', title || ' ' || content)) STORED
);
CREATE INDEX idx_posts_search ON posts1 USING GIN (searchable_index_col);
-- 查询时:
SELECT title, content FROM posts1 WHERE searchable_index_col @@ to_tsquery('english', 'PostgreSQL');
-- 中文分词 (需使用 Docker 镜像并初始化)
CREATE EXTENSION IF NOT EXISTS zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
2. 地理信息 (PostGIS / earthdistance)
sql
-- 安装扩展
CREATE EXTENSION cube;
CREATE EXTENSION earthdistance;
-- 计算 (经度1, 纬度1) 到 (经度2, 纬度2) 的距离
SELECT earth_distance(
ll_to_earth(23.45, 113.12),
ll_to_earth(23.50, 113.15)
) AS distance_meters;
-- PostGIS 高级用法:坐标系转换
-- ST_Transform(geometry, 4326)
3. 向量数据库 (pgvector)
bash
# Docker 运行 pgvector
docker pull pgvector/pgvector:pg16
docker run -d --name pg-vector -e POSTGRES_PASSWORD=root -p 15432:5432 pgvector/pgvector:pg16
- 三大核心操作符 :
<=>(余弦距离,RAG 最常用)、<->(L2 欧氏距离)、<#>(内积)。
第七章:生产运维篇 ------ 安全与备份
1. 用户与角色 (RBAC) 代码
sql
-- 1. 创建一个组 (不准登录)
CREATE ROLE managers;
-- 2. 创建一个用户 (准许登录)
CREATE USER fengfeng WITH PASSWORD 'fengfeng';
-- 3. 让“用户”加入“组”
GRANT managers TO fengfeng;
-- 示例:给用户设置某一个库的只读权限
-- 1. 创建用户
CREATE USER reader_user WITH PASSWORD 'fengfeng';
-- 2. 基础进门权
GRANT CONNECT ON DATABASE your_db_name TO reader_user; -- 可以进入db这个库
GRANT USAGE ON SCHEMA public TO reader_user; -- 可以进入public这个模式
-- 3. 核心只读权 (现在的表 + 序列)
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader_user;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO reader_user;
-- 4. 自动化 (未来的表)
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO reader_user;
2. 备份与恢复代码
bash
# pg_dump 单库备份 (纯文本 SQL 格式)
pg_dump -U postgres -d db > db_0414.sql
# pg_dump 单库备份 (压缩的二进制格式,推荐:体积小,支持并行恢复)
pg_dump -U postgres -F c -d db > db_0414.bak
# pg_dumpall 全库备份 (包含所有数据库、用户和权限)
set PGPASSWORD=root
pg_dumpall -U postgres > all_databases_backup.sql
# 恢复纯文本 SQL 备份
psql -U postgres -d db1 < db_0414.sql
# 恢复二进制备份 (必须用 pg_restore)
pg_restore -U postgres -d db2 -c db_0414.bak
# -c 代表先清理(删除)旧表再恢复,防止冲突
# 恢复 pg_dumpall 的全库备份
psql -U postgres -f all_databases_backup.sql postgres