PostgreSQL 常用扩展 #

扩展概述 #

PostgreSQL 的扩展系统是其强大功能的重要来源。

text
┌─────────────────────────────────────────────────────────────┐
│                    扩展类型                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  数据类型扩展                                                │
│  ├── PostGIS    - 地理空间数据                              │
│  ├── hstore     - 键值存储                                  │
│  └── citext     - 大小写不敏感文本                          │
│                                                             │
│  功能扩展                                                    │
│  ├── pg_stat_statements - SQL 统计                         │
│  ├── pg_trgm     - 相似度搜索                               │
│  └── pgcrypto    - 加密函数                                 │
│                                                             │
│  性能扩展                                                    │
│  ├── pg_prewarm  - 预热缓存                                 │
│  └── pg_buffercache - 缓存监控                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

扩展管理 #

查看和安装扩展 #

sql
-- 查看可用扩展
SELECT * FROM pg_available_extensions;

-- 查看已安装扩展
SELECT * FROM pg_extension;

-- 安装扩展
CREATE EXTENSION extension_name;

-- 安装特定版本
CREATE EXTENSION extension_name VERSION '1.0';

-- 安装到特定模式
CREATE EXTENSION extension_name SCHEMA my_schema;

-- 更新扩展
ALTER EXTENSION extension_name UPDATE;

-- 删除扩展
DROP EXTENSION extension_name;

PostGIS #

安装和基本使用 #

sql
-- 安装 PostGIS
CREATE EXTENSION postgis;

-- 检查版本
SELECT PostGIS_Version();

-- 创建空间数据表
CREATE TABLE locations (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    geom GEOMETRY(Point, 4326)
);

-- 插入点数据
INSERT INTO locations (name, geom) VALUES
    ('Beijing', ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326)),
    ('Shanghai', ST_SetSRID(ST_MakePoint(121.4737, 31.2304), 4326)),
    ('Guangzhou', ST_SetSRID(ST_MakePoint(113.2644, 23.1291), 4326));

-- 创建空间索引
CREATE INDEX idx_locations_geom ON locations USING gist(geom);

空间查询 #

sql
-- 计算距离(米)
SELECT 
    name,
    ST_Distance(
        geom::geography,
        ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326)::geography
    ) AS distance_meters
FROM locations
ORDER BY distance_meters;

-- 查找附近的位置
SELECT name 
FROM locations 
WHERE ST_DWithin(
    geom::geography,
    ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326)::geography,
    500000  -- 500 公里
);

-- 创建多边形
CREATE TABLE areas (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    geom GEOMETRY(Polygon, 4326)
);

INSERT INTO areas (name, geom) VALUES
    ('Zone A', ST_MakePolygon(ST_GeomFromText(
        'LINESTRING(116.0 39.0, 117.0 39.0, 117.0 40.0, 116.0 40.0, 116.0 39.0)', 4326
    )));

-- 点是否在多边形内
SELECT l.name
FROM locations l, areas a
WHERE ST_Within(l.geom, a.geom);

pg_stat_statements #

安装和配置 #

sql
-- 安装扩展
CREATE EXTENSION pg_stat_statements;

-- 需要在 postgresql.conf 中配置
-- shared_preload_libraries = 'pg_stat_statements'
-- pg_stat_statements.track = all

-- 重启 PostgreSQL 后生效

查询统计 #

sql
-- 查看最慢的查询
SELECT 
    calls,
    round(total_exec_time::numeric, 2) AS total_time_ms,
    round(mean_exec_time::numeric, 2) AS avg_time_ms,
    round((100 * total_exec_time / sum(total_exec_time) over())::numeric, 2) AS percent,
    query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

-- 查看执行次数最多的查询
SELECT 
    calls,
    round(mean_exec_time::numeric, 2) AS avg_time_ms,
    query
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 10;

-- 查看最耗 IO 的查询
SELECT 
    calls,
    shared_blks_hit,
    shared_blks_read,
    query
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 10;

-- 重置统计
SELECT pg_stat_statements_reset();

pg_trgm #

安装和使用 #

sql
-- 安装扩展
CREATE EXTENSION pg_trgm;

-- 相似度计算
SELECT similarity('hello', 'hallo');
-- 0.6

-- 显示相似度
SELECT show_trgm('hello');
-- {"  h"," he",ell,hel,llo,"lo "}

-- 相似度搜索
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100)
);

INSERT INTO products (name) VALUES
    ('iPhone 15 Pro'),
    ('iPhone 14 Pro'),
    ('Samsung Galaxy'),
    ('Google Pixel');

-- 创建 GIN 索引
CREATE INDEX idx_products_name_trgm ON products USING gin(name gin_trgm_ops);

-- 相似度搜索
SELECT name, similarity(name, 'iphone') AS sim
FROM products
WHERE name % 'iphone'
ORDER BY sim DESC;

-- 使用 % 操作符(相似度阈值)
SELECT name FROM products WHERE name % 'iphone';

-- 使用 <% 操作符(相似度排序)
SELECT name FROM products WHERE name <% 'iphone' ORDER BY name <-> 'iphone';

相似度阈值设置 #

sql
-- 查看当前阈值
SELECT show_limit();

-- 设置阈值(0-1)
SET pg_trgm.similarity_threshold = 0.3;

-- 或在配置文件中设置
-- pg_trgm.similarity_threshold = 0.3

pgcrypto #

加密函数 #

sql
-- 安装扩展
CREATE EXTENSION pgcrypto;

-- MD5 哈希
SELECT md5('password');

-- SHA-256 哈希
SELECT encode(digest('password', 'sha256'), 'hex');

-- bcrypt 密码哈希
SELECT crypt('password', gen_salt('bf'));

-- 验证密码
SELECT crypt('password', '$2a$06$...') = crypt('password', '$2a$06$...');

-- 生成随机盐
SELECT gen_salt('bf');  -- bcrypt
SELECT gen_salt('bf', 10);  -- 指定成本因子

-- AES 加密
SELECT encrypt('secret data', 'my_key', 'aes');
SELECT decrypt(encrypt('secret data', 'my_key', 'aes'), 'my_key', 'aes');

-- PGP 加密
SELECT pgp_sym_encrypt('secret data', 'my_password');
SELECT pgp_sym_decrypt(pgp_sym_encrypt('secret data', 'my_password'), 'my_password');

存储密码示例 #

sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    password_hash TEXT
);

-- 插入用户(使用 bcrypt)
INSERT INTO users (username, password_hash)
VALUES ('alice', crypt('mypassword', gen_salt('bf')));

-- 验证密码
SELECT * FROM users 
WHERE username = 'alice' 
  AND password_hash = crypt('mypassword', password_hash);

hstore #

键值存储 #

sql
-- 安装扩展
CREATE EXTENSION hstore;

-- 创建表
CREATE TABLE user_attributes (
    id SERIAL PRIMARY KEY,
    user_id INTEGER,
    attrs HSTORE
);

-- 插入数据
INSERT INTO user_attributes (user_id, attrs) VALUES
    (1, '"theme"=>"dark", "language"=>"zh-CN", "notifications"=>"true"'),
    (2, '"theme"=>"light", "language"=>"en-US"');

-- 获取值
SELECT attrs -> 'theme' FROM user_attributes WHERE user_id = 1;

-- 设置值
UPDATE user_attributes 
SET attrs = attrs || '"timezone"=>"Asia/Shanghai"'::hstore
WHERE user_id = 1;

-- 删除键
UPDATE user_attributes 
SET attrs = delete(attrs, 'timezone')
WHERE user_id = 1;

-- 查询包含键的行
SELECT * FROM user_attributes WHERE attrs ? 'theme';

-- 查询键值匹配
SELECT * FROM user_attributes WHERE attrs @> '"theme"=>"dark"';

-- 获取所有键
SELECT skeys(attrs) FROM user_attributes;

-- 获取所有值
SELECT svals(attrs) FROM user_attributes;

-- 转换为 JSON
SELECT hstore_to_json(attrs) FROM user_attributes;

citext #

大小写不敏感文本 #

sql
-- 安装扩展
CREATE EXTENSION citext;

-- 创建表
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username CITEXT UNIQUE,
    email CITEXT
);

-- 插入数据
INSERT INTO users (username, email) VALUES
    ('Alice', 'ALICE@example.com'),
    ('Bob', 'bob@example.com');

-- 大小写不敏感查询
SELECT * FROM users WHERE username = 'alice';  -- 可以找到 Alice
SELECT * FROM users WHERE email = 'Alice@Example.com';  -- 可以找到 ALICE@example.com

-- UNIQUE 约束也是大小写不敏感的
-- INSERT INTO users (username) VALUES ('alice');  -- 会失败,因为 Alice 已存在

uuid-ossp #

UUID 生成 #

sql
-- 安装扩展
CREATE EXTENSION "uuid-ossp";

-- 生成 UUID v4(随机)
SELECT uuid_generate_v4();

-- 生成 UUID v1(基于时间戳)
SELECT uuid_generate_v1();

-- 在表中使用
CREATE TABLE sessions (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
    user_id INTEGER,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- PostgreSQL 13+ 内置函数
SELECT gen_random_uuid();

pg_buffercache #

缓存监控 #

sql
-- 安装扩展
CREATE EXTENSION pg_buffercache;

-- 查看缓存使用情况
SELECT 
    c.relkind,
    c.relname,
    count(*) AS buffers,
    pg_size_pretty(count(*) * 8192::bigint) AS size
FROM pg_buffercache b
JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid)
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
GROUP BY c.relkind, c.relname
ORDER BY count(*) DESC
LIMIT 20;

-- 查看表的缓存命中率
SELECT 
    relname,
    heap_blks_read,
    heap_blks_hit,
    round(heap_blks_hit::numeric / NULLIF(heap_blks_hit + heap_blks_read, 0) * 100, 2) AS hit_ratio
FROM pg_statio_user_tables
WHERE heap_blks_read + heap_blks_hit > 0
ORDER BY hit_ratio DESC;

pg_prewarm #

缓存预热 #

sql
-- 安装扩展
CREATE EXTENSION pg_prewarm;

-- 预热表到缓存
SELECT pg_prewarm('employees');

-- 预热特定类型
SELECT pg_prewarm('employees', 'main');  -- 主数据
SELECT pg_prewarm('employees', 'fsm');   -- Free Space Map
SELECT pg_prewarm('employees', 'vm');    -- Visibility Map

-- 预热索引
SELECT pg_prewarm('idx_employees_email');

-- 预热到指定缓冲区
SELECT pg_prewarm('employees', 'buffer');

tablefunc #

交叉表(透视表) #

sql
-- 安装扩展
CREATE EXTENSION tablefunc;

-- 创建销售数据
CREATE TABLE sales (
    year INTEGER,
    quarter INTEGER,
    amount DECIMAL(10, 2)
);

INSERT INTO sales VALUES
    (2024, 1, 10000), (2024, 2, 15000), (2024, 3, 12000), (2024, 4, 18000),
    (2025, 1, 11000), (2025, 2, 16000), (2025, 3, 13000), (2025, 4, 19000);

-- 交叉表查询
SELECT * FROM crosstab(
    'SELECT year, quarter, amount FROM sales ORDER BY 1, 2',
    'SELECT DISTINCT quarter FROM sales ORDER BY 1'
) AS ct(year INTEGER, q1 DECIMAL, q2 DECIMAL, q3 DECIMAL, q4 DECIMAL);

-- 输出:
-- year |   q1   |   q2   |   q3   |   q4
-- -----+--------+--------+--------+--------
-- 2024 | 10000  | 15000  | 12000  | 18000
-- 2025 | 11000  | 16000  | 13000  | 19000

学习路径 #

text
扩展阶段
├── JSON操作
├── 全文搜索
└── 常用扩展(本文)

总结 #

PostgreSQL 的扩展系统极大地增强了其功能。通过合理使用扩展,可以:

  • 使用 PostGIS 处理地理空间数据
  • 使用 pg_stat_statements 分析查询性能
  • 使用 pg_trgm 实现模糊搜索
  • 使用 pgcrypto 进行数据加密
  • 使用 hstore 存储键值对
  • 使用 citext 处理大小写不敏感文本

选择合适的扩展可以大大简化开发工作,提高系统性能!

最后更新:2026-03-29