世界上最先进的开源关系型数据库
PostgreSQL(简称Postgres)是功能强大的开源关系型数据库管理系统,支持SQL标准,提供ACID事务、外键、触发器、视图、存储过程等高级特性。
空间和地理对象支持
时间序列数据优化
模糊文本搜索
加密功能
键值对存储
查询性能统计
# 使用 Homebrew
brew install postgresql@14
# 启动服务
brew services start postgresql@14
# 初始化数据库(如果首次安装)
initdb /usr/local/var/postgres
# 创建数据库
createdb mydb
# 连接
psql mydb
# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib
# 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql
# 切换用户并连接
sudo -u postgres psql
# 修改postgres用户密码
ALTER USER postgres PASSWORD 'newpassword';
# 创建数据库
createdb mydb
# 配置远程访问(编辑 postgresql.conf)
listen_addresses = '*'
# 连接设置
listen_addresses = '*'
port = 5432
max_connections = 200
# 内存设置
shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 64MB
maintenance_work_mem = 1GB
# WAL设置
wal_buffers = 16MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
min_wal_size = 1GB
# 日志设置
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_statement = 'all'
# 查询性能
random_page_cost = 1.1
effective_io_concurrency = 200
配置文件位置:/etc/postgresql/14/main/postgresql.conf
# 拉取镜像
docker pull postgres:14
# 运行容器
docker run --name postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-p 5432:5432 \
-v /data/postgres:/var/lib/postgresql/data \
-d postgres:14
# 连接
docker exec -it postgres psql -U postgres
# 带扩展的镜像
docker pull timescale/timescaledb:latest-pg14
# Docker Compose
version: '3.8'
services:
postgres:
image: postgres:14
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
ports:
- "5432:5432"
volumes:
- ./data:/var/lib/postgresql/data
-- 创建数据库
CREATE DATABASE mydb
WITH ENCODING 'UTF8'
LC_COLLATE = 'en_US.UTF-8'
LC_CTYPE = 'en_US.UTF-8';
-- 使用数据库
\c mydb
-- 创建表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
age INTEGER CHECK (age >= 0 AND age <= 150),
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_username ON users(username);
CREATE INDEX idx_email ON users(email);
CREATE UNIQUE INDEX idx_email_unique ON users(email);
-- 修改表
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users RENAME COLUMN age TO age_years;
ALTER TABLE users ALTER COLUMN age TYPE INTEGER USING age::INTEGER;
-- 删除表
DROP TABLE IF EXISTS users CASCADE;
-- 插入数据
INSERT INTO users (username, email, password_hash, age)
VALUES
('john', 'john@example.com', 'hash123', 25),
('jane', 'jane@example.com', 'hash456', 30);
-- 查询数据
SELECT * FROM users;
SELECT username, email FROM users WHERE age > 25;
SELECT username, COUNT(*) FROM users GROUP BY username;
-- 更新数据
UPDATE users
SET email = 'newemail@example.com'
WHERE username = 'john';
-- 删除数据
DELETE FROM users WHERE status = 'inactive';
-- 事务
BEGIN;
INSERT INTO users (username, email) VALUES ('test', 'test@example.com');
UPDATE users SET age = 29 WHERE username = 'test';
COMMIT;
ROLLBACK; -- 回滚
-- 创建JSONB字段
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
specs JSONB
);
-- 插入JSON数据
INSERT INTO products (name, specs) VALUES
('iPhone', '{"storage": "128GB", "color": "black", "price": 999}');
-- 查询JSON
SELECT specs->>'color' FROM products;
SELECT specs->'price' FROM products;
-- JSON运算符
SELECT * FROM products WHERE specs @> '{"color": "black"}';
SELECT * FROM products WHERE specs ? 'price';
SELECT * FROM products WHERE specs ?| ARRAY['color', 'storage'];
-- JSONB索引(GIN索引)
CREATE INDEX idx_specs ON products USING GIN (specs);
-- JSON函数
SELECT jsonb_pretty(specs) FROM products;
SELECT jsonb_extract_path(specs, 'storage', '128GB') FROM products;
-- 创建带数组的表
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
tags VARCHAR[] DEFAULT '{}',
colors TEXT[],
prices NUMERIC[]
);
-- 插入数组数据
INSERT INTO products (name, tags, colors) VALUES
('iPhone', ARRAY['mobile', 'electronics'],
ARRAY['black', 'white', 'blue']);
-- 查询数组
SELECT * FROM products WHERE 'electronics' = ANY(tags);
SELECT * FROM products WHERE tags @> ARRAY['mobile'];
SELECT unnest(tags) FROM products;
-- 数组函数
SELECT array_length(tags, 1) FROM products;
SELECT array_append(tags, 'new-tag') FROM products;
SELECT array_cat(tags, ARRAY['tag1', 'tag2']) FROM products;
-- ROW_NUMBER、RANK、DENSE_RANK
SELECT
name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) as row_num,
RANK() OVER (ORDER BY salary DESC) as rank,
DENSE_RANK() OVER (ORDER BY salary DESC) as dense_rank
FROM employees;
-- 分区内排名
SELECT
department,
name,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) as dept_rank
FROM employees;
-- 累计统计
SELECT
date,
sales,
SUM(sales) OVER (ORDER BY date) as cumulative_sales,
AVG(sales) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as moving_avg
FROM daily_sales;
-- LAG/LEAD
SELECT
date,
sales,
LAG(sales, 1) OVER (ORDER BY date) as prev_sales,
LEAD(sales, 1) OVER (ORDER BY date) as next_sales
FROM daily_sales;
-- 递归查询树形结构
WITH RECURSIVE categories AS (
-- 锚点:顶级分类
SELECT id, name, parent_id, 0 as level
FROM category
WHERE parent_id IS NULL
UNION ALL
-- 递归:子分类
SELECT c.id, c.name, c.parent_id, cat.level + 1
FROM category c
INNER JOIN categories cat ON c.parent_id = cat.id
)
SELECT * FROM categories;
-- 路径追踪
WITH RECURSIVE employee_hierarchy AS (
SELECT id, name, manager_id, name as path
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id,
eh.path || ' > ' || e.name
FROM employees e
INNER JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT * FROM employee_hierarchy;
-- B-tree索引(默认)
CREATE INDEX idx_email ON users(email);
-- GiST索引(地理数据)
CREATE INDEX idx_location ON places USING GiST (location);
-- GIN索引(JSONB、数组)
CREATE INDEX idx_specs ON products USING GIN (specs);
CREATE INDEX idx_tags ON products USING GIN (tags);
-- BRIN索引(大表)
CREATE INDEX idx_timestamp ON logs USING BRIN (created_at);
-- 部分索引
CREATE INDEX idx_active_users ON users(email)
WHERE status = 'active';
-- 表达式索引
CREATE INDEX idx_lower_email ON users(lower(email));
-- 复合索引
CREATE INDEX idx_name_age ON users(lastname, firstname, age);
-- 创建存储过程
CREATE OR REPLACE FUNCTION get_user_orders(
user_id INTEGER,
OUT order_count INTEGER,
OUT total_amount NUMERIC
) AS $$
BEGIN
SELECT COUNT(*), SUM(amount)
INTO order_count, total_amount
FROM orders
WHERE user_id = get_user_orders.user_id;
END;
$$ LANGUAGE plpgsql;
-- 调用存储过程
SELECT * FROM get_user_orders(1);
-- 创建触发器
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER update_users_timestamp
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
-- 范围分区
CREATE TABLE orders (
id SERIAL,
order_date DATE,
amount NUMERIC
) PARTITION BY RANGE (order_date);
-- 创建分区表
CREATE TABLE orders_2024_q1 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE orders_2024_q2 PARTITION OF orders
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
-- 哈希分区
CREATE TABLE users (
id SERIAL,
username VARCHAR(50)
) PARTITION BY HASH (id);
CREATE TABLE users_p0 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE users_p1 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
-- 分析查询计划
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';
-- 详细执行统计
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
-- JSON格式输出
EXPLAIN (FORMAT JSON, ANALYZE)
SELECT * FROM products WHERE specs @> '{"color": "black"}';
-- 常见计划节点
-- Seq Scan - 顺序扫描(避免)
-- Index Scan - 索引扫描(推荐)
-- Bitmap Scan - 位图扫描
-- Nested Loop - 嵌套循环
-- Hash Join - 哈希连接
-- Sort - 排序
# 内存配置(服务器8GB RAM示例)
shared_buffers = 2GB # 25% RAM
effective_cache_size = 6GB # 50-75% RAM
work_mem = 64MB # 每个操作
maintenance_work_mem = 1GB # 维护操作
# WAL配置
wal_buffers = 16MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
min_wal_size = 1GB
# 连接配置
max_connections = 200
shared_buffers = max_connections * 2MB
# 查询优化
random_page_cost = 1.1 # SSD环境
effective_io_concurrency = 200 # SSD环境
default_statistics_target = 100
# 并行查询
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
max_worker_processes = 8
-- 查看连接状态
SELECT * FROM pg_stat_activity;
-- 查看慢查询
SELECT * FROM pg_stat_statements
ORDER BY total_time DESC LIMIT 10;
-- 查看表统计
SELECT * FROM pg_stat_user_tables
WHERE schemaname = 'public';
-- 查看索引使用情况
SELECT * FROM pg_stat_user_indexes;
-- 查看表膨胀
SELECT schemaname, tablename,
n_dead_tup, n_live_tup
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000;
-- VACUUM和ANALYZE
VACUUM ANALYZE users;
VACUUM FULL users;
-- 用户表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 商品表(使用JSONB存储规格)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
price NUMERIC(10,2) NOT NULL,
stock INTEGER DEFAULT 0,
specs JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_specs ON products USING GIN (specs);
-- 订单表
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
total_amount NUMERIC(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_created_at ON orders(created_at);
-- 按月统计销售额(窗口函数)
SELECT
DATE_TRUNC('month', created_at) as month,
SUM(total_amount) as monthly_sales,
AVG(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', created_at)
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as moving_avg
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY DATE_TRUNC('month', created_at);
-- Top 10 用户(子查询)
SELECT u.username, o.total_spent
FROM (
SELECT user_id, SUM(total_amount) as total_spent
FROM orders
GROUP BY user_id
ORDER BY total_spent DESC
LIMIT 10
) o
JOIN users u ON o.user_id = u.id;
-- 商品推荐(基于共同购买)
SELECT DISTINCT p2.id, p2.name
FROM order_items oi1
JOIN order_items oi2 ON oi1.order_id = oi2.order_id
JOIN products p1 ON oi1.product_id = p1.id
JOIN products p2 ON oi2.product_id = p2.id
WHERE p1.id = 123 AND p2.id != p1.id
LIMIT 10;
-- 启用PostGIS扩展
CREATE EXTENSION postgis;
-- 创建地理位置表
CREATE TABLE places (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
location GEOGRAPHY(POINT, 4326)
);
CREATE INDEX idx_location ON places USING GIST (location);
-- 插入地理位置数据
INSERT INTO places (name, location) VALUES
('Beijing', 'POINT(116.4074 39.9042)'),
('Shanghai', 'POINT(121.4737 31.2304)');
-- 查找附近的地点
SELECT name,
ST_Distance(location, 'POINT(116.4074 39.9042)') as distance
FROM places
ORDER BY location <-> 'POINT(116.4074 39.9042)'
LIMIT 10;
-- 查找指定范围内的地点
SELECT name
FROM places
WHERE ST_DWithin(
location,
'POINT(116.4074 39.9042)'::geography,
50000 -- 50km
);