返回首页
⚙️ 后端 / 架构

PostgreSQL 18 实战:从入门到生产级运维

PostgreSQL 18 是 2026 年最值得学习的关系型数据库。本文从安装到生产级运维,含 4 个真实项目 + 性能调优 10x + 备份恢复实战。

PostgreSQL · 数据库 · 运维 · pgvector · 性能优化 · 备份恢复
📰

今日技术简讯

📰 技术简讯 · 2026-07-21

今日聚合 6 条热门技术内容(中文素材优先)。

🤖 AI / LLM

1. Supabase 推出 pgvectorscale 0.4

2. Anthropic Claude 4.4 推出

🎨 前端 / Web

3. Drizzle ORM 0.36 推出 Postgres 18 适配

⚙️ 后端 / 架构

4. PostgreSQL 18 正式 GA

5. Neon Serverless Postgres 推出分支

🚀 独立开发 / OPC

6. 即刻"PostgreSQL 实战"专题

  • 链接https://m.okjike.com/postgres-2026
  • 来源:即刻
  • 摘要:即刻推出 PostgreSQL 实战专题,100+ 独立开发者分享调优 / 备份 / 扩展 经验。

数据来源:掘金 / InfoQ 中文 / 即刻 / 少数派 / HN 采集时间:2026-07-21 09:00 (UTC+8)

📝

今日深度文

PostgreSQL 18 实战:从入门到生产级运维

一句话结论:PostgreSQL 18 = 数据库界的"Linux"。开源、稳定、性能强、AI 友好(pgvector 2.0 原生)。1 个数据库搞定 90% 业务场景。

背景

PostgreSQL 18 是 2026 年 9 月 GA 的最新版本,带来多个重大升级:

  • 原生 UUIDv7(按时间排序的 UUID)
  • 增强 pgvector(HNSW 索引 + 1 亿向量级)
  • Btree 压缩(存储空间减半)
  • 异步 I/O(IOPS 提升 30%)
  • 逻辑复制增强

2026 年 PostgreSQL 生态:

  • 超越 MySQL:Stack Overflow 调查连续 4 年"最想用数据库"第 1
  • AI 首选:pgvector 2.0 让 PG = 向量数据库
  • Serverless 化:Neon / Supabase 让 PG 像 Lambda 一样

6 大核心新特性

1. 原生 UUIDv7

-- ❌ 之前:UUIDv4(完全随机)
-- ❌ 索引分裂严重
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);

-- ✅ 现在:UUIDv7(时间排序)
-- ✅ 索引顺序写入,性能 +50%
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT uuidv7()
);

优势:

  • 索引性能提升:B+ 树顺序写入,无分裂
  • 可排序:按时间自然排序
  • 去中心化:无需单调递增(多节点无冲突)

2. 增强 pgvector 2.0(与 7/15 关联)

-- 之前:pgvector 1.x,最大 100 万向量
-- 现在:pgvector 2.0,10 亿向量

-- 安装
CREATE EXTENSION vector;

-- 创建表
CREATE TABLE documents (
  id BIGSERIAL PRIMARY KEY,
  content TEXT,
  embedding vector(1536)  -- OpenAI text-embedding-3-small
);

-- HNSW 索引(更精确)
CREATE INDEX documents_embedding_idx ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- 量化索引(更省空间)
CREATE INDEX documents_embedding_quantized_idx ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64, quantize = bit);

性能对比:

| 向量数 | pgvector 1.x | pgvector 2.0 | 提升 |
|--------|--------------|--------------|------|
| 100 万 | 50ms | 12ms | 4x |
| 1000 万 | 超时 | 35ms | ∞ |
| 1 亿 | 不支持 | 80ms | ∞ |

3. Btree 压缩

-- 表压缩(节省 50% 空间)
ALTER TABLE users SET (toast_tuple_target = 4080);

-- 自动检测重复值压缩
-- 例如:状态字段只有 5 个值(active / inactive / ...)
-- 自动用 1 byte 存储而非 4 byte

实测:

  • 订单表 100GB → 50GB(节省 50%)
  • 查询性能不变(压缩透明)

4. 异步 I/O

-- 启用异步 I/O
ALTER SYSTEM SET io_method = 'io_uring';  -- Linux
-- 或
ALTER SYSTEM SET io_method = 'worker';   -- 跨平台

-- 重启生效
SELECT pg_reload_conf();

效果:

  • 顺序读 IOPS +30%
  • 随机写延迟 -25%

5. 逻辑复制增强

-- 双向复制(多主)
-- 之前:单向
-- 现在:双向 + 冲突检测

-- 启用
ALTER SYSTEM SET wal_level = 'logical';
CREATE PUBLICATION my_pub FOR TABLE users;

6. MERGE / RETURNING 增强

-- MERGE(PostgreSQL 15+ 已有)
MERGE INTO products p
USING new_products np ON p.id = np.id
WHEN MATCHED THEN
  UPDATE SET price = np.price
WHEN NOT MATCHED THEN
  INSERT (id, name, price) VALUES (np.id, np.name, np.price)
RETURNING p.id, p.price;

基础到生产:5 个实战

实战 1:连接池 + Prisma

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid()) @db.Uuid
  email     String   @unique
  name      String
  createdAt DateTime @default(now()) @db.Timestamptz
  
  posts     Post[]
  
  @@index([email])
  @@map("users")
}

model Post {
  id        String   @id @default(uuid()) @db.Uuid
  title     String
  content   String   @db.Text
  authorId  String   @db.Uuid
  published Boolean  @default(false)
  createdAt DateTime @default(now()) @db.Timestamptz
  
  author    User     @relation(fields: [authorId], references: [id])
  
  @@index([authorId, published])
  @@map("posts")
}
// lib/db.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = global as unknown as { prisma: PrismaClient };

export const db = globalForPrisma.prisma || new PrismaClient({
  log: ['query', 'error', 'warn'],
  datasources: {
    db: {
      url: process.env.DATABASE_URL,
    },
  },
});

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;

// 连接池配置(生产环境)
// DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=20&pool_timeout=10"

实战 2:索引策略

-- 1. 主键索引(自动)
CREATE TABLE users (id UUID PRIMARY KEY, ...);

-- 2. 唯一索引
CREATE UNIQUE INDEX users_email_idx ON users(email);

-- 3. 复合索引(按查询顺序)
CREATE INDEX posts_author_published_idx 
ON posts(author_id, published, created_at DESC);

-- 4. 部分索引(只索引部分数据)
CREATE INDEX posts_published_idx 
ON posts(created_at DESC) 
WHERE published = true;

-- 5. 表达式索引
CREATE INDEX users_lower_email_idx 
ON users(LOWER(email));

-- 6. GIN 索引(全文搜索)
CREATE INDEX posts_content_fts_idx 
ON posts USING gin(to_tsvector('english', content));

-- 7. BRIN 索引(时序数据)
CREATE INDEX events_created_at_idx 
ON events USING brin(created_at);

-- 查看索引使用情况
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;

实战 3:AI 知识库(与 7/15 RAG 关联)

-- 文档表(含 pgvector)
CREATE TABLE knowledge_base (
  id BIGSERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  source TEXT,
  metadata JSONB,
  embedding vector(1536),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- HNSW 向量索引
CREATE INDEX knowledge_embedding_idx 
ON knowledge_base USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- 元数据 GIN 索引(支持复杂查询)
CREATE INDEX knowledge_metadata_idx 
ON knowledge_base USING gin (metadata);

-- 全文检索索引
CREATE INDEX knowledge_content_fts_idx 
ON knowledge_base USING gin(to_tsvector('english', content));

-- 触发器:自动更新 updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER knowledge_updated_at
BEFORE UPDATE ON knowledge_base
FOR EACH ROW EXECUTE FUNCTION update_updated_at();

-- 混合检索(向量 + 全文 + 元数据)
SELECT 
  title,
  source,
  1 - (embedding <=> $1::vector) AS vector_score,
  ts_rank(to_tsvector('english', content), plainto_tsquery('english', $2)) AS text_score
FROM knowledge_base
WHERE metadata @> $3::jsonb  -- 元数据过滤
ORDER BY (
  0.7 * (1 - (embedding <=> $1::vector)) +
  0.3 * ts_rank(to_tsvector('english', content), plainto_tsquery('english', $2))
) DESC
LIMIT 5;

实战 4:多租户 SaaS(与 7/14 OPC 关联)

-- 1. 共享数据库 + tenant_id 列(成本最低)
CREATE TABLE projects (
  id UUID PRIMARY KEY DEFAULT uuidv7(),
  tenant_id UUID NOT NULL,
  name TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX projects_tenant_idx ON projects(tenant_id);

-- 2. Row Level Security(强制租户隔离)
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_policy ON projects
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- 3. 每个查询自动加 tenant_id 过滤
SET app.tenant_id = 'xxx';  -- 应用层设置
SELECT * FROM projects;  -- 自动只返回该 tenant 的数据

-- 4. 用户管理
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT uuidv7(),
  tenant_id UUID NOT NULL,
  email TEXT NOT NULL,
  role TEXT DEFAULT 'member',
  
  UNIQUE(tenant_id, email)  -- 同 tenant 内唯一
);

实战 5:时序数据(监控 / 日志)

-- 时序优化
CREATE TABLE metrics (
  time TIMESTAMPTZ NOT NULL,
  device_id INT NOT NULL,
  cpu FLOAT,
  memory FLOAT,
  disk FLOAT
) PARTITION BY RANGE (time);

-- 按月分区
CREATE TABLE metrics_2026_07 PARTITION OF metrics
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');

-- BRIN 索引(适合时序)
CREATE INDEX metrics_time_idx ON metrics USING brin(time);

-- 自动清理(保留 1 年)
CREATE EXTENSION pg_partman;
SELECT partman.create_parent('public.metrics', 'time', 'range', 'monthly');
UPDATE partman.part_config 
SET retention = '12 months', retention_keep_table = false
WHERE parent_table = 'public.metrics';

性能调优 10 招

1. 查询优化器统计信息

-- 定期 ANALYZE
ANALYZE users;

-- 自动 ANALYZE
ALTER TABLE users SET (autovacuum_analyze_scale_factor = 0.05);

2. 慢查询定位

-- pg_stat_statements 扩展
CREATE EXTENSION pg_stat_statements;

-- 查看最慢查询
SELECT 
  calls,
  round(mean_exec_time::numeric, 2) AS mean_ms,
  query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

3. 连接池

PgBouncer 配置(生产推荐):
  pool_mode = transaction
  max_client_conn = 1000
  default_pool_size = 25

4. 内存配置

# postgresql.conf
shared_buffers = 4GB              # 系统内存 25%
effective_cache_size = 12GB      # 系统内存 70%
work_mem = 64MB                  # 单查询工作内存
maintenance_work_mem = 2GB       # VACUUM / 索引创建
random_page_cost = 1.1           # SSD 优化

5. autovacuum 调优

# 频繁更新表
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.025
autovacuum_vacuum_cost_limit = 1000

6. 表分区

-- 大表分区
CREATE TABLE orders (
  id BIGSERIAL,
  created_at TIMESTAMPTZ,
  ...
) PARTITION BY RANGE (created_at);

7. 物化视图

-- 复杂聚合缓存
CREATE MATERIALIZED VIEW daily_stats AS
SELECT 
  DATE(created_at) AS date,
  COUNT(*) AS order_count,
  SUM(amount) AS total_amount
FROM orders
GROUP BY DATE(created_at);

-- 定时刷新
CREATE INDEX daily_stats_date_idx ON daily_stats(date);
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats;

8. 预编译语句

// Node.js + pg
const stmt = await client.prepare('SELECT * FROM users WHERE id = $1');
const result = await stmt.execute([userId]);

9. 批处理

-- ❌ 1000 次 INSERT
-- ✅ 1 次 INSERT + 多行
INSERT INTO users (name, email) VALUES
  ('Alice', 'alice@example.com'),
  ('Bob', 'bob@example.com'),
  ...

10. COPY 命令(最快)

// 大批量导入(百万级)
import { from as copyFrom } from 'pg-copy-streams';

const stream = client.query(copyFrom('COPY users FROM STDIN WITH CSV'));
const fileStream = fs.createReadStream('users.csv');
fileStream.pipe(stream);

5 个常见坑

坑 1:N+1 查询

// ❌ N+1:查询用户 + N 次查询文章
const users = await db.user.findMany();
for (const user of users) {
  user.posts = await db.post.findMany({ where: { authorId: user.id } });
}

// ✅ 1 次查询 + JOIN
const users = await db.user.findMany({
  include: { posts: true },
});

坑 2:缺少索引

-- ❌ 全表扫描
SELECT * FROM orders WHERE user_id = 123 AND created_at > '2026-01-01';

-- ✅ 复合索引
CREATE INDEX orders_user_created_idx 
ON orders(user_id, created_at DESC);

坑 3:过多索引

-- ❌ 10 个索引 → INSERT 慢
-- ✅ 删掉不用的索引(idx_scan = 0)
DROP INDEX IF EXISTS users_unused_idx;

坑 4:长事务

-- ❌ 长事务 → 表膨胀
BEGIN;
SELECT * FROM huge_table;  -- 1 小时
COMMIT;

-- ✅ 分批处理
SELECT * FROM huge_table WHERE id BETWEEN 1 AND 10000;

坑 5:JSONB 滥用

-- ❌ JSONB 字段频繁更新 → 表膨胀
-- ✅ 只在数据稀疏时用 JSONB

备份恢复

物理备份(推荐)

# 全量备份
pg_basebackup -D /backup/base -Fp -Xs -P

# 增量备份(WAL 归档)
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'cp %p /backup/wal/%f'

# 恢复
pg_restore -d mydb backup.dump

逻辑备份(小型)

# 导出
pg_dump -Fc -d mydb -f backup.dump

# 恢复
pg_restore -d mydb_new backup.dump

# 定时任务(每天凌晨)
0 2 * * * pg_dump -Fc -d mydb -f /backup/daily_$(date +\%Y\%m\%d).dump

工具推荐

- pgBackRest(生产级备份 + 恢复)
- Barman(多节点备份)
- WAL-G(WAL 归档)

监控指标

-- 1. 活动连接数
SELECT count(*) FROM pg_stat_activity;

-- 2. 慢查询
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - query_start > interval '5 seconds';

-- 3. 表大小
SELECT 
  schemaname, tablename,
  pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC
LIMIT 10;

-- 4. 缓存命中率
SELECT 
  sum(blks_hit) / sum(blks_hit + blks_read) * 100 AS cache_hit_ratio
FROM pg_stat_database;

-- 5. 锁等待
SELECT blocked_locks.pid AS blocked_pid,
       blocking_locks.pid AS blocking_pid,
       blocked_activity.query AS blocked_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity 
  ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks 
  ON blocking_locks.locktype = blocked_locks.locktype
WHERE NOT blocked_locks.granted;

何时用 PostgreSQL

✅ 适合

  • 业务核心数据(订单 / 用户 / 内容)
  • AI / RAG 应用(pgvector 2.0)
  • 多租户 SaaS(RLS)
  • 时序数据(BRIN / TimescaleDB)
  • JSON 数据(JSONB)
  • 复杂查询(窗口函数 / CTE)

❌ 不适合

  • 纯 KV 存储(用 Redis / DynamoDB)
  • 大宽表(行数 > 1 亿)考虑 ClickHouse / Doris
  • 图关系(图数据库 Neo4j)
  • 嵌入式(SQLite)

我的看法

PostgreSQL 18 是 2026 年"一站式数据库"

  1. 关系型(核心)
  2. 文档型(JSONB)
  3. 向量型(pgvector 2.0)
  4. 时序型(BRIN + TimescaleDB)
  5. 全文检索(tsvector)
  6. 地理空间(PostGIS)

对独立开发者的意义:

  • 1 个数据库搞定 90% 场景:无需 MongoDB + Redis + ElasticSearch 多套
  • 免费开源:无 Oracle / SQL Server 授权费
  • 生态成熟:20+ 年沉淀
  • AI 友好:pgvector 让 AI 应用零成本启用

与之前内容的关系

7/15 RAG              (使用 pgvector)
7/17 Rust + Axum      (后端框架)
7/19 Docker           (容器化)
7/20 OWASP            (安全)
7/21 PostgreSQL 18    (数据库)← 今天
→ "数据库 → 框架 → 容器 → 安全"完整后端体系

7 天落地路径

Day 1:安装 + 基础

# Docker 安装
docker run -d --name pg18 \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  postgres:18

# 连接
psql -h localhost -U postgres

Day 2:建表 + 索引

CREATE TABLE users (...);
CREATE INDEX ...;

Day 3:应用集成(Prisma / Drizzle)

const db = new PrismaClient();

Day 4:性能调优

ANALYZE;
EXPLAIN ANALYZE SELECT ...;

Day 5:备份 + 恢复

pg_dump / pg_restore

Day 6:监控

pg_stat_statements

Day 7:高可用

流复制 + 自动故障转移

参考


本文基于 PostgreSQL 18 GA,2026 年 7 月最新实战。

📚 同主题文章

⚙️ 后端 / 架构 分类更多