TL;DR: 本文介绍 PostgreSQL 的核心特性、安装配置、基本操作,以及 JSON 支持、全文搜索、窗口函数等高级功能。读完即可上手使用。
假设读者熟悉 SQL 基础,了解至少一种关系型数据库。
为什么选择 PostgreSQL Link to heading
PostgreSQL 是一个开源的对象关系型数据库系统,以稳定性、扩展性和标准合规著称。相比 MySQL 等竞品,它的核心优势在于:
- 严格的 ACID 合规性:事务隔离、崩溃恢复可靠
- 丰富的数据类型:原生支持数组、JSON/JSONB、几何类型、UUID 等
- 强大的扩展机制:支持自定义类型、函数、操作符,以及 PostGIS 等知名扩展
- 高级查询能力:CTE、窗口函数、物化视图、并行查询
根据 Stack Overflow 2024 开发者调查,PostgreSQL 已连续多年成为开发者"最想要的数据库"。
安装与配置 Link to heading
安装 Link to heading
macOS (Homebrew):
brew install postgresql@16
brew services start postgresql@16
Ubuntu/Debian:
sudo apt install postgresql-16
sudo systemctl start postgresql
Docker:
docker run --name pg-demo -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres:16
连接数据库 Link to heading
psql -U postgres -h localhost
连接后创建测试数据库:
CREATE DATABASE demo_db;
\c demo_db
基本操作 Link to heading
创建表 Link to heading
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
settings JSONB DEFAULT '{}'
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(200) NOT NULL,
content TEXT,
published_at TIMESTAMPTZ,
view_count INT DEFAULT 0
);
插入与查询 Link to heading
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com'),
('bob', 'bob@example.com');
INSERT INTO posts (user_id, title, content, published_at) VALUES
(1, 'PostgreSQL 入门', '这是一篇教程...', NOW()),
(1, '高级 SQL 技巧', '窗口函数很有用...', NOW()),
(2, '数据库优化', '索引是关键...', NOW());
常用查询:
-- JOIN 查询
SELECT u.username, p.title, p.published_at
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.published_at IS NOT NULL
ORDER BY p.published_at DESC;
-- 聚合统计
SELECT u.username, COUNT(p.id) AS post_count, SUM(p.view_count) AS total_views
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.username
HAVING COUNT(p.id) > 0;
索引 Link to heading
-- B-tree 索引(默认)
CREATE INDEX idx_posts_user_id ON posts(user_id);
-- 部分索引(只索引已发布的文章)
CREATE INDEX idx_published ON posts(published_at) WHERE published_at IS NOT NULL;
-- 复合索引
CREATE INDEX idx_posts_user_published ON posts(user_id, published_at);
使用 EXPLAIN ANALYZE 验证索引效果:
EXPLAIN ANALYZE SELECT * FROM posts WHERE user_id = 1;
高级功能概览 Link to heading
JSON/JSONB 支持 Link to heading
PostgreSQL 原生支持 JSON 存储和查询,兼具关系型与文档型数据库的优势:
-- 查询 JSON 字段
SELECT username, settings->>'theme' AS theme
FROM users
WHERE settings->>'theme' = 'dark';
-- 更新 JSON 字段
UPDATE users
SET settings = jsonb_set(settings, '{theme}', '"light"')
WHERE username = 'alice';
-- 在 JSON 字段上创建 GIN 索引
CREATE INDEX idx_users_settings ON users USING GIN (settings);
适合场景:动态配置、半结构化数据、快速迭代的原型阶段。
全文搜索 Link to heading
无需引入 Elasticsearch,PostgreSQL 内置全文搜索能力:
-- 创建搜索向量和索引
ALTER TABLE posts ADD COLUMN search_vector tsvector;
UPDATE posts SET search_vector =
to_tsvector('chinese', COALESCE(title, '') || ' ' || COALESCE(content, ''));
CREATE INDEX idx_posts_search ON posts USING GIN (search_vector);
-- 全文搜索
SELECT title, content
FROM posts
WHERE search_vector @@ to_tsquery('chinese', '教程 & 技巧');
-- 带高亮的搜索
SELECT ts_headline('chinese', content, to_tsquery('chinese', '索引'),
'StartSel = <b>, StopSel = </b>') AS highlighted
FROM posts
WHERE search_vector @@ to_tsquery('chinese', '索引');
对于中文搜索,建议安装 zhparser 扩展配合 pg_jieba 分词。
窗口函数 Link to heading
窗口函数在不改变结果集行数的情况下进行聚合计算:
-- 排名
SELECT title, view_count,
RANK() OVER (ORDER BY view_count DESC) AS rank,
ROW_NUMBER() OVER (ORDER BY published_at DESC) AS row_num
FROM posts;
-- 移动平均
SELECT published_at, view_count,
AVG(view_count) OVER (ORDER BY published_at ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM posts;
-- 分区统计
SELECT username, title, view_count,
SUM(view_count) OVER (PARTITION BY username) AS user_total_views
FROM posts p
JOIN users u ON p.user_id = u.id;
CTE 与递归查询 Link to heading
-- CTE 提高可读性
WITH active_users AS (
SELECT id, username FROM users WHERE created_at > NOW() - INTERVAL '30 days'
)
SELECT au.username, COUNT(p.id) AS posts
FROM active_users au
JOIN posts p ON au.id = p.user_id
GROUP BY au.username;
-- 递归 CTE:处理树形结构
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, 1 AS depth
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY depth, name;
物化视图 Link to heading
-- 创建物化视图
CREATE MATERIALIZED VIEW user_stats AS
SELECT u.id, u.username, COUNT(p.id) AS post_count, COALESCE(SUM(p.view_count), 0) AS total_views
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id, u.username;
-- 刷新数据
REFRESH MATERIALIZED VIEW user_stats;
-- 并发刷新(不阻塞查询)
REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats;
-- 注意:需要 UNIQUE 索引
扩展:PostGIS Link to heading
空间数据支持是 PostgreSQL 的杀手级特性之一:
CREATE EXTENSION IF NOT EXISTS postgis;
-- 存储地理位置
CREATE TABLE places (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
location GEOMETRY(Point, 4326)
);
INSERT INTO places (name, location)
VALUES ('Beijing', ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326));
-- 查找附近的位置(50km 内)
SELECT name FROM places
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326),
0.5
);
实用技巧 Link to heading
常用 psql 命令 Link to heading
\dt -- 列出所有表
\d table -- 查看表结构
\l -- 列出所有数据库
\di -- 列出所有索引
\du -- 列出所有用户
\timing -- 开启执行时间显示
\explain -- 分析查询计划
性能调优要点 Link to heading
- shared_buffers:建议设为系统内存的 25%
- work_mem:控制排序和哈希操作的内存,默认 4MB 偏低
- effective_cache_size:告诉优化器有多少内存可用于缓存
- 使用
pg_stat_statements扩展定位慢查询
总结 Link to heading
PostgreSQL 的核心优势在于功能全面且稳定可靠。对于大多数应用场景,它既能满足传统关系型需求,又能通过 JSONB 处理半结构化数据,通过全文搜索替代专用搜索引擎,通过 PostGIS 处理空间数据。
选择建议:
- 新项目默认选 PostgreSQL,除非团队已有 MySQL 深厚积累
- 需要 GIS 能力时,PostGIS 是不二之选
- 高并发写入场景可配合连接池(PgBouncer)
延伸阅读 Link to heading
本文由 AI 生成