PostgreSQL 原理、架构与设计思想详解
世界上最先进的开源关系型数据库系统
info PostgreSQL 简介
历史发展
- 1986年:POSTGRES项目启动
- 1995年:添加SQL解释器,发布Postgres95
- 1996年:正式更名为PostgreSQL
- 2010年:支持NoSQL特性,如JSON数据类型
- 2017年:支持逻辑复制和分区表
- 2020年:支持增强的并行查询和JIT编译
- 2025年:引入异步I/O框架,进一步提升性能
architecture PostgreSQL 架构
进程结构
- Postmaster进程:主进程,负责启动和关闭数据库服务器,管理其他进程
- 服务器进程:处理客户端连接,每个客户端连接对应一个服务器进程
- 后台进程:执行特定任务的后台进程,如检查点进程、WAL写入进程、自动清理进程等
- 辅助进程:如统计收集进程、归档进程等
# 查看PostgreSQL进程 ps aux | grep postgres典型输出示例:
postgres 1234 0.0 0.1 123456 7890 ? S 10:00 0:00 /usr/lib/postgresql/14/bin/postgres
postgres 1235 0.0 0.0 123456 2345 ? Ss 10:00 0:00 postgres: logger
postgres 1237 0.0 0.0 123456 3456 ? Ss 10:00 0:00 postgres: checkpointer
postgres 1238 0.0 0.0 123456 4567 ? Ss 10:00 0:00 postgres: writer
postgres 1239 0.0 0.0 123456 5678 ? Ss 10:00 0:00 postgres: wal writer
postgres 1240 0.0 0.0 123456 6789 ? Ss 10:00 0:00 postgres: autovacuum launcher
postgres 1241 0.0 0.0 123456 7890 ? Ss 10:00 0:00 postgres: stats collector
postgres 1242 0.0 0.0 123456 8901 ? Ss 10:00 0:00 postgres: logical replication launcher
内存结构
- 共享内存:由所有后端进程共享,主要包括共享缓冲区、WAL缓冲区、锁表等
- 本地内存:每个后端进程独有,包括工作内存、临时缓冲区等
存储结构
- 表空间:逻辑存储单元,可以映射到操作系统的不同目录
- 数据库:表空间的子集,包含多个模式
- 模式:数据库的逻辑分组,包含表、索引、函数等对象
- 表和索引:实际存储数据的结构,以堆表形式组织
-- 查看表空间信息
SELECT spcname AS "表空间名称",
pg_tablespace_location(oid) AS "位置"
FROM pg_tablespace;-- 查看数据库存储信息
SELECT datname AS "数据库名称",
pg_database_size(datname) AS "大小"
FROM pg_database
ORDER BY pg_database_size(datname) DESC;
settings PostgreSQL 核心原理
多版本并发控制 (MVCC)
-- 查看系统列,了解MVCC实现 SELECT xmin, xmax, ctid, * FROM your_table LIMIT 5;-- xmin: 创建该行版本的事务ID -- xmax: 删除或更新该行版本的事务ID -- ctid: 行的物理位置,包含块号和块内偏移量
- xmin:插入或更新该行版本的事务ID
- xmax:删除或更新该行版本的事务ID,初始为null
- 事务状态:存储在CLOG(Transaction Commit Log)中,包含事务的状态(in-progress, committed, aborted)
预写日志 (WAL)
- WAL记录:描述对数据库所做的更改
- WAL缓冲区:内存中的WAL记录缓冲区
- WAL段文件:磁盘上的WAL文件,默认大小为16MB
- 检查点(Checkpoint):定期将缓冲区中的脏页写入磁盘,并记录检查点位置
-- 查看WAL配置参数 SHOW wal_level; SHOW wal_buffers; SHOW checkpoint_timeout; SHOW max_wal_size;-- 查看WAL文件信息 SELECT pg_walfile_name(pg_current_wal_lsn());
- 事务修改数据时,先将修改记录到WAL缓冲区
- WAL写入进程定期将WAL缓冲区的内容写入磁盘上的WAL段文件
- 当事务提交时,确保WAL记录已写入磁盘
- 后台进程定期将缓冲区中的脏页写入磁盘(检查点)
- 系统崩溃后,通过重放检查点后的WAL记录来恢复数据
查询优化器
- 解析和重写查询
- 生成可能的执行路径
- 估算每个执行路径的成本
- 选择成本最低的执行计划
-- 查看查询执行计划 EXPLAIN SELECT * FROM employees WHERE department_id = 10;-- 查看详细的执行计划和成本估算 EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 10;
- I/O成本:从磁盘读取数据页的成本
- CPU成本:处理数据的计算成本
- 内存使用:执行计划所需的内存量
- 网络开销:在分布式环境中的网络传输成本
- 扫描方法:顺序扫描(Seq Scan)、索引扫描(Index Scan)、位图扫描(Bitmap Scan)等
- 连接策略:嵌套循环连接(Nested Loop)、哈希连接(Hash Join)、归并连接(Merge Join)等
lightbulb PostgreSQL 设计思想
可扩展性
- 数据类型扩展:用户可以定义自定义数据类型,包括复合类型、枚举类型、范围类型等
- 函数扩展:支持多种编程语言编写函数,如PL/pgSQL、PL/Python、PL/Perl等
- 索引扩展:支持自定义索引方法,如GiST、SP-GiST、GIN等
- 外部数据包装器(FDW):可以访问外部数据源,如Oracle、MySQL、MongoDB等
-- 创建自定义数据类型
CREATE TYPE address AS (
street VARCHAR(100),
city VARCHAR(50),
zip_code VARCHAR(10)
);-- 创建自定义函数
CREATE OR REPLACE FUNCTION calculate_tax(numeric) RETURNS numeric AS $$
BEGIN
RETURN $1 * 0.08;
END;
$$ LANGUAGE plpgsql;
-- 使用扩展
CREATE EXTENSION postgis; -- 地理信息系统扩展
CREATE EXTENSION pg_stat_statements; -- 查询统计扩展
标准合规性
- SQL标准支持:支持大部分SQL:2011标准特性
- 事务隔离级别:支持四种标准隔离级别:读未提交、读已提交、可重复读和串行化
- 数据完整性:支持外键约束、唯一约束、检查约束等
可靠性与数据完整性
- WAL机制:确保事务的持久性,即使系统崩溃也能恢复数据
- 事务ACID特性:严格支持原子性、一致性、隔离性和持久性
- 数据完整性约束:支持主键、外键、唯一、检查等多种约束
- 崩溃恢复:提供完善的崩溃恢复机制,确保数据一致性
开源与社区驱动
- 透明性:所有开发过程公开透明,任何人都可以参与
- 多样性:来自不同背景的贡献者带来多样化的思想和解决方案
- 持续创新:社区驱动的开发模式促进了持续的创新和改进
- 用户反馈:直接从用户社区获取反馈,快速响应需求
stars PostgreSQL 特性与优势
丰富的数据类型
| 数据类型类别 | 具体类型 | 特点 |
|---|---|---|
| 基本类型 | 整数、浮点数、字符、日期时间等 | 标准SQL数据类型 |
| 几何类型 | 点、线、多边形、圆等 | 支持空间数据操作 |
| 网络地址类型 | inet, cidr, macaddr等 | 专门用于存储网络地址 |
| 文本搜索类型 | tsvector, tsquery | 支持全文搜索 |
| JSON类型 | json, jsonb | 支持JSON数据存储和查询 |
| 数组类型 | 任意类型的数组 | 支持数组操作和索引 |
| 范围类型 | int4range, daterange等 | 支持范围查询 |
| 自定义类型 | 复合类型、枚举类型等 | 用户可定义自己的数据类型 |
-- JSON类型示例
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
attributes JSONB
);INSERT INTO products (name, attributes) VALUES
('Laptop', '{"color": "silver", "weight": "1.5kg", "price": 999.99}'),
('Phone', '{"color": "black", "weight": "0.2kg", "price": 699.99}');
-- 查询JSON数据
SELECT name, attributes->>'color' AS color, attributes->>'price' AS price
FROM products
WHERE (attributes->>'price')::numeric > 800;
-- 数组类型示例
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(100),
tags TEXT[]
);
INSERT INTO posts (title, tags) VALUES
('PostgreSQL Guide', ARRAY['database', 'postgres', 'sql']),
('Database Design', ARRAY['database', 'design']);
-- 查询数组数据
SELECT title, tags
FROM posts
WHERE 'database' = ANY(tags);
高级索引功能
| 索引类型 | 适用场景 | 特点 |
|---|---|---|
| B-tree | 等值查询、范围查询 | 默认索引类型,适用于大多数场景 |
| Hash | 等值查询 | 仅适用于等值查询,比B-tree更节省空间 |
| GiST | 地理数据、全文搜索 | 通用搜索树,支持多种数据类型 |
| SP-GiST | 空间分区数据 | 空间分区通用搜索树,适用于非平衡数据结构 |
| GIN | 多值类型(数组、JSON、全文搜索) | 广义倒排索引,适用于包含多个键的值 |
| BRIN | 线性排序的大表 | 块范围索引,适用于按物理顺序存储的大表 |
-- 创建不同类型的索引 -- B-tree索引 CREATE INDEX idx_products_name ON products(name);-- GIN索引(用于JSONB) CREATE INDEX idx_products_attributes ON products USING GIN(attributes);
-- 部分索引 CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
-- 表达式索引 CREATE INDEX idx_users_lower_email ON users(lower(email));
-- 复合索引 CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
并发与性能
- MVCC机制:读写操作不互相阻塞,提高并发性能
- 并行查询:支持并行顺序扫描、并行连接、并行聚合等操作
- 表分区:支持声明式分区,提高大表的查询性能
- JIT编译:即时编译表达式,提高复杂查询性能
- 连接池:支持pgBouncer等连接池工具,减少连接开销
-- 启用并行查询 SET max_parallel_workers_per_gather = 4;-- 查看并行查询执行计划 EXPLAIN ANALYZE SELECT * FROM large_table WHERE condition;
-- 创建分区表 CREATE TABLE measurement ( city_id int, log_date date, peaktemp int, unitsales int ) PARTITION BY RANGE (log_date);
-- 创建分区 CREATE TABLE measurement_y2023 PARTITION OF measurement FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');
CREATE TABLE measurement_y2024 PARTITION OF measurement FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
高可用与复制
- 流复制:基于WAL的物理复制,支持同步和异步模式
- 逻辑复制:基于行变更的逻辑复制,支持选择性复制和跨版本复制
- 故障转移:支持自动故障转移工具如Patroni、repmgr等
- 负载均衡:支持读写分离和负载均衡
-- 配置主库进行流复制 -- 在postgresql.conf中设置: wal_level = replica max_wal_senders = 3 max_replication_slots = 3-- 在pg_hba.conf中添加复制连接: host replication replicator 192.168.1.0/24 md5
-- 创建复制用户 CREATE USER replicator REPLICATION LOGIN PASSWORD 'password';
-- 在备库上设置恢复配置 standby_mode = on primary_conninfo = 'host=primary_host port=5432 user=replicator password=password'
business PostgreSQL 应用场景
企业级应用
- ERP系统:处理复杂的业务逻辑和大量事务
- CRM系统:管理客户数据和交互历史
- 人力资源系统:管理员工信息和组织结构
- 财务系统:处理财务数据和报表
地理信息系统(GIS)
- 地图服务:存储和查询地理空间数据
- 位置分析:基于地理位置的数据分析
- 路径规划:计算最优路径和距离
- 地理围栏:定义和检测地理边界
-- 安装PostGIS扩展 CREATE EXTENSION postgis;-- 创建包含地理数据的表 CREATE TABLE locations ( id SERIAL PRIMARY KEY, name VARCHAR(100), geom GEOMETRY(Point, 4326) );
-- 插入地理数据 INSERT INTO locations (name, geom) VALUES ('北京', ST_GeomFromText('POINT(116.4074 39.9042)', 4326)), ('上海', ST_GeomFromText('POINT(121.4737 31.2304)', 4326));
-- 查询距离北京100公里内的位置 SELECT name, ST_Distance(geom, ST_GeomFromText('POINT(116.4074 39.9042)', 4326)) AS distance FROM locations WHERE ST_DWithin(geom, ST_GeomFromText('POINT(116.4074 39.9042)', 4326), 0.9); -- 0.9度约等于100公里
数据分析与商业智能
- 数据仓库:存储和分析大量历史数据
- 报表系统:生成复杂的业务报表
- OLAP分析:多维数据分析
- 数据挖掘:发现数据中的模式和趋势
-- 窗口函数示例
SELECT
department_id,
employee_id,
salary,
AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salary,
salary - AVG(salary) OVER (PARTITION BY department_id) AS difference_from_avg
FROM employees;-- 公用表表达式(CTE)示例
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region, product, SUM(quantity) AS product_units
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
Web应用
- 内容管理系统:存储和管理网站内容
- 电子商务平台:处理产品、订单和支付数据
- 社交网络:管理用户关系和互动数据
- SaaS应用:多租户数据隔离和管理
物联网(IoT)与时序数据
- 传感器数据:存储和分析大量传感器读数
- 监控系统:实时监控和告警
- 设备追踪:记录和分析设备状态
- 预测性维护:基于历史数据预测设备故障