纲要
- PostgreSQL 扩展性机制与 AI 生态
- 核心向量检索扩展
pgvector:官方生态最流行的向量存储与检索扩展pgvecto.rs/VectorChord:高性能向量检索扩展pg_analytics/pg_lakehouse:分析型查询引擎
- 相似性搜索与文本处理扩展
pg_similarity:文本相似性计算pg_summarize:LLM 文本摘要pg_tokenize:分词与 Token 计数
- 库内机器学习扩展
PGforML:PostgreSQL 内建机器学习postgresML:SQL 驱动的 ML/AI 框架
- 生态优势与架构决策
引言
PostgreSQL 凭借其强大的扩展性(extensibility)机制,早已超越传统关系型数据库的边界。在 AI 与向量检索(vector search)领域,PG 生态中涌现出大量高质量的扩展插件,使得开发者可以在不引入独立向量数据库的前提下,直接在 PG 内完成向量数据的存储、索引与相似性检索。本文系统梳理 PG 生态中与 AI、向量检索相关的核心扩展,涵盖其功能定位、适用场景与技术选型建议。
核心向量检索扩展
pgvector
pgvector 是 PostgreSQL 生态中最流行的开源向量相似性搜索扩展。它通过在 PG 中引入新的 vector 数据类型,使数据库能够原生存储和操作向量嵌入(vector embeddings)。
核心能力:
- 支持
vector(单精度)、halfvec(半精度)、binary(二进制)及sparsevec(稀疏向量)等多种向量类型 - 提供多种距离度量函数:L2 距离(欧氏距离)、内积(inner product)、余弦距离(cosine distance)、L1 距离、汉明距离(Hamming distance)及 Jaccard 距离
- 支持精确最近邻搜索(exact nearest neighbor search)与近似最近邻搜索(approximate nearest neighbor search)
- 支持 IVFFlat 与 HNSW 两种索引算法
安装与基础使用(适用版本:PostgreSQL 13+,pgvector 0.8.6):
-- 启用扩展(每个数据库执行一次)
CREATE EXTENSION vector;
-- 创建包含向量列的表,指定维度为 3
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding vector(3)
);
-- 插入向量数据
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
-- 按 L2 距离查询最近邻
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
-- 创建 HNSW 索引以加速查询(推荐默认使用 HNSW)
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
-- 创建 IVFFlat 索引(需先有数据)
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);
距离算子说明:
| 算子 | 距离类型 | 说明 |
|---|---|---|
<-> | L2 距离 | 欧氏距离,最常用 |
<#> | 内积 | 返回负内积(因 PG 仅支持 ASC 索引扫描) |
<=> | 余弦距离 | 余弦相似度 |
<+> | L1 距离 | 曼哈顿距离 |
<~> | 汉明距离 | 仅适用于二进制向量 |
<%> | Jaccard 距离 | 仅适用于二进制向量 |
维度限制说明:
pgvector 的 vector 类型默认最大支持 2000 维,这是由于 PostgreSQL 默认 8KB 页大小限制所致。自 0.7.0 版本起,可通过 halfvec 类型索引最高 4000 维的向量。
pgvecto.rs 与 VectorChord
pgvecto.rs 是一款用 Rust 编写、基于 pgrx 框架开发的 PostgreSQL 向量检索扩展。其官方团队后续推出了性能与稳定性更优的 VectorChord,官方建议新用户迁移至 VectorChord。
核心特性:
- 最大支持 65535 维向量
- 支持 FP16、INT8 及二进制向量等多种数据类型
- SIMD 指令动态分发,最大化硬件性能
- 引入 VBASE 方法处理向量检索与关系查询(如过滤、JOIN)的混合场景
快速启动(Docker):
docker run \
--name pgvecto-rs-demo \
-e POSTGRES_PASSWORD=mysecretpassword \
-p 5432:5432 \
-d ghcr.io/tensorchord/pgvecto-rs:pg17-v0.4.0
-- 启用扩展
DROP EXTENSION IF EXISTS vectors;
CREATE EXTENSION vectors;
-- 创建向量表
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding vector(768)
);
pgvecto.rs / VectorChord 相较于 pgvector 的主要优势在于高维支持与混合查询能力,适合需要处理高维 embedding(如 768 维、1024 维)且对过滤条件有严格要求的场景。
pg_analytics 与 pg_lakehouse
pg_analytics 是 ParadeDB 团队开发的分析型查询引擎,目前已演进为 pg_lakehouse,可将 Postgres 转换为对外部对象存储(如 S3)和表格式(如 Delta Lake)的分析查询引擎。在向量检索场景中,该扩展可用于大规模向量数据的分析型工作负载。
相似性搜索与文本处理扩展
pg_similarity
pg_similarity 专注于文本相似性计算,提供多种相似度算法(如集合相似度、模糊搜索等),适用于文本去重、数据清洗等场景。该扩展侧重于非向量类的文本相似性判断,与向量检索形成互补。
pg_summarize
pg_summarize 是基于 pgrx 框架的 LLM 文本摘要扩展,允许用户在 PostgreSQL 内直接调用大模型生成文本摘要,属于 AI 应用层与 SQL 的结合。
pg_tokenize
pg_tokenize 提供分词与 Token 计数功能,可在 SQL 中直接进行文本分词,配合 RAG(Retrieval-Augmented Generation)系统进行文档分片与 Token 长度控制。
库内机器学习扩展
PGforML
PGforML 由国内开发者郭老师实现,允许在 PostgreSQL 数据库内直接进行机器学习模型的训练与推理,减少数据迁移开销。
postgresML
postgresML 是业界领先的 PostgreSQL AI 框架之一,提供 SQL 驱动的机器学习能力,支持在数据库内调用和运行深度学习模型。
生态优势与架构决策
在 PG 中直接实现向量检索能力,相较于引入独立向量数据库(如 Milvus、Pinecone)具有以下优势:
- 数据一致性:避免跨系统的数据同步与一致性问题
- 运维简化:减少额外组件的部署、监控与维护成本
- 统一查询:可在同一 SQL 中融合向量检索与关系查询
- ACID 保障:继承 PostgreSQL 的事务、点时间恢复等企业级特性
API 速览
pgvector 核心 API
数据类型:vector(n)
定义一个 n 维向量列。
CREATE TABLE table_name (
column_name vector(n)
);
距离算子:
| 算子 | 用途 | 示例 |
|---|---|---|
<-> | L2 距离 | ORDER BY embedding <-> '[1,2,3]' |
<#> | 内积(负值) | ORDER BY embedding <#> '[1,2,3]' |
<=> | 余弦距离 | ORDER BY embedding <=> '[1,2,3]' |
索引创建:
-- HNSW 索引(推荐)
CREATE INDEX ON table_name USING hnsw (column_name vector_cosine_ops);
-- IVFFlat 索引
CREATE INDEX ON table_name USING ivfflat (column_name vector_l2_ops) WITH (lists = 100);
pgvecto.rs 核心 API
数据类型:vector(n)
CREATE TABLE table_name (
column_name vector(n)
);
启用扩展:
CREATE EXTENSION vectors;
Demo 简单示例
以下是一个基于 Node.js 和 pgvector 的完整示例,演示向量存储与相似性检索。
运行环境要求:
- PostgreSQL 13+ 已安装
pgvector扩展 - Node.js 16+
项目结构:
pgvector-demo
├── package.json
├── index.js
└── .env
package.json:
{
"name": "pgvector-demo",
"version": "1.0.0",
"description": "pgvector 向量检索示例",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"pg": "^8.11.0",
"dotenv": "^16.3.0"
}
}
index.js:
const { Client } = require('pg');
require('dotenv').config();
const client = new Client({
host: process.env.PGHOST || 'localhost',
port: process.env.PGPORT || 5432,
user: process.env.PGUSER || 'postgres',
password: process.env.PGPASSWORD || 'postgres',
database: process.env.PGDATABASE || 'postgres',
});
async function main() {
await client.connect();
// 1. 启用 pgvector 扩展
await client.query('CREATE EXTENSION IF NOT EXISTS vector;');
// 2. 创建表
await client.query(`
DROP TABLE IF EXISTS demo_items;
CREATE TABLE demo_items (
id SERIAL PRIMARY KEY,
embedding vector(3),
label TEXT
);
`);
// 3. 插入向量数据
const vectors = [
{ embedding: '[1, 1, 1]', label: 'A' },
{ embedding: '[2, 2, 2]', label: 'B' },
{ embedding: '[3, 3, 3]', label: 'C' },
{ embedding: '[10, 10, 10]', label: 'D' },
];
for (const v of vectors) {
await client.query(
'INSERT INTO demo_items (embedding, label) VALUES ($1, $2)',
[v.embedding, v.label]
);
}
// 4. 创建 HNSW 索引
await client.query(`
CREATE INDEX IF NOT EXISTS idx_demo_items_hnsw
ON demo_items USING hnsw (embedding vector_l2_ops);
`);
// 5. 查询最近邻(L2 距离)
const queryVector = '[2.5, 2.5, 2.5]';
const result = await client.query(
`
SELECT id, label, embedding,
embedding <-> $1 AS distance
FROM demo_items
ORDER BY embedding <-> $1
LIMIT 3
`,
[queryVector]
);
console.log('查询向量:', queryVector);
console.log('最近邻结果:');
result.rows.forEach(row => {
console.log(` label: ${row.label}, distance: ${row.distance.toFixed(4)}`);
});
await client.end();
}
main().catch(console.error);
对应的 PostgreSQL 原生指令(注释参考):
-- 启用扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 建表
CREATE TABLE demo_items (
id SERIAL PRIMARY KEY,
embedding vector(3),
label TEXT
);
-- 插入数据
INSERT INTO demo_items (embedding, label) VALUES
('[1,1,1]', 'A'),
('[2,2,2]', 'B'),
('[3,3,3]', 'C'),
('[10,10,10]', 'D');
-- 创建 HNSW 索引
CREATE INDEX ON demo_items USING hnsw (embedding vector_l2_ops);
-- L2 距离查询
SELECT id, label, embedding,
embedding <-> '[2.5,2.5,2.5]' AS distance
FROM demo_items
ORDER BY embedding <-> '[2.5,2.5,2.5]'
LIMIT 3;
运行说明:
npm install
node index.js
技术点总结:
pgvector扩展的启用与向量列定义- 向量数据的插入与 HNSW 索引创建
- 基于 L2 距离的最近邻查询
- Node.js 中使用
pg驱动操作向量数据
多语言示例
以下基于相同的 PostgreSQL + pgvector 环境,分别提供 Go、Python、Java 的完整示例代码,与前述 Node.js 示例实现相同的功能:启用扩展、建表、插入向量数据、创建 HNSW 索引、执行 L2 距离最近邻查询。
Go 示例
项目结构:
pgvector-go-demo
├── go.mod
├── main.go
└── .env
go.mod:
module pgvector-go-demo
go 1.21
require (
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
)
main.go:
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
)
func main() {
// 加载 .env
_ = godotenv.Load()
connStr := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
getEnv("PGHOST", "localhost"),
getEnv("PGPORT", "5432"),
getEnv("PGUSER", "postgres"),
getEnv("PGPASSWORD", "postgres"),
getEnv("PGDATABASE", "postgres"),
)
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatal(err)
}
// 1. 启用扩展
_, err = db.Exec("CREATE EXTENSION IF NOT EXISTS vector;")
if err != nil {
log.Fatal(err)
}
// 2. 建表
_, err = db.Exec(`
DROP TABLE IF EXISTS demo_items;
CREATE TABLE demo_items (
id SERIAL PRIMARY KEY,
embedding vector(3),
label TEXT
);
`)
if err != nil {
log.Fatal(err)
}
// 3. 插入数据
vectors := []struct {
embedding string
label string
}{
{"[1,1,1]", "A"},
{"[2,2,2]", "B"},
{"[3,3,3]", "C"},
{"[10,10,10]", "D"},
}
for _, v := range vectors {
_, err = db.Exec(
"INSERT INTO demo_items (embedding, label) VALUES ($1, $2)",
v.embedding, v.label,
)
if err != nil {
log.Fatal(err)
}
}
// 4. 创建 HNSW 索引
_, err = db.Exec(`
CREATE INDEX IF NOT EXISTS idx_demo_items_hnsw
ON demo_items USING hnsw (embedding vector_l2_ops);
`)
if err != nil {
log.Fatal(err)
}
// 5. 查询最近邻
queryVector := "[2.5,2.5,2.5]"
rows, err := db.Query(`
SELECT id, label, embedding,
embedding <-> $1 AS distance
FROM demo_items
ORDER BY embedding <-> $1
LIMIT 3
`, queryVector)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
fmt.Println("查询向量:", queryVector)
fmt.Println("最近邻结果:")
for rows.Next() {
var id int
var label string
var embedding string
var distance float64
if err := rows.Scan(&id, &label, &embedding, &distance); err != nil {
log.Fatal(err)
}
fmt.Printf(" label: %s, distance: %.4f\n", label, distance)
}
}
func getEnv(key, fallback string) string {
if val := os.Getenv(key); val != "" {
return val
}
return fallback
}
运行说明:
go mod tidy
go run main.go
Python 示例
项目结构:
pgvector-py-demo
├── requirements.txt
├── main.py
└── .env
requirements.txt:
psycopg2-binary==2.9.9
python-dotenv==1.0.0
main.py:
import os
import psycopg2
from dotenv import load_dotenv
load_dotenv()
def main():
conn = psycopg2.connect(
host=os.getenv("PGHOST", "localhost"),
port=os.getenv("PGPORT", "5432"),
user=os.getenv("PGUSER", "postgres"),
password=os.getenv("PGPASSWORD", "postgres"),
database=os.getenv("PGDATABASE", "postgres"),
)
conn.autocommit = True
cur = conn.cursor()
# 1. 启用扩展
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
# 2. 建表
cur.execute("""
DROP TABLE IF EXISTS demo_items;
CREATE TABLE demo_items (
id SERIAL PRIMARY KEY,
embedding vector(3),
label TEXT
);
""")
# 3. 插入数据
vectors = [
("[1,1,1]", "A"),
("[2,2,2]", "B"),
("[3,3,3]", "C"),
("[10,10,10]", "D"),
]
for emb, label in vectors:
cur.execute(
"INSERT INTO demo_items (embedding, label) VALUES (%s, %s)",
(emb, label),
)
# 4. 创建 HNSW 索引
cur.execute("""
CREATE INDEX IF NOT EXISTS idx_demo_items_hnsw
ON demo_items USING hnsw (embedding vector_l2_ops);
""")
# 5. 查询最近邻
query_vector = "[2.5,2.5,2.5]"
cur.execute("""
SELECT id, label, embedding,
embedding <-> %s AS distance
FROM demo_items
ORDER BY embedding <-> %s
LIMIT 3
""", (query_vector, query_vector))
print("查询向量:", query_vector)
print("最近邻结果:")
for row in cur.fetchall():
print(f" label: {row[1]}, distance: {row[3]:.4f}")
cur.close()
conn.close()
if __name__ == "__main__":
main()
运行说明:
pip install -r requirements.txt
python main.py
Java 示例
项目结构:
pgvector-java-demo
├── pom.xml
└── src/main/java/com/example/Main.java
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>pgvector-java-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
</project>
Main.java:
package com.example;
import io.github.cdimascio.dotenv.Dotenv;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Main {
public static void main(String[] args) {
Dotenv dotenv = Dotenv.configure().ignoreIfMissing().load();
String host = dotenv.get("PGHOST", "localhost");
String port = dotenv.get("PGPORT", "5432");
String user = dotenv.get("PGUSER", "postgres");
String password = dotenv.get("PGPASSWORD", "postgres");
String database = dotenv.get("PGDATABASE", "postgres");
String url = String.format(
"jdbc:postgresql://%s:%s/%s",
host, port, database
);
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 1. 启用扩展
try (PreparedStatement stmt = conn.prepareStatement(
"CREATE EXTENSION IF NOT EXISTS vector;")) {
stmt.execute();
}
// 2. 建表
try (PreparedStatement stmt = conn.prepareStatement("""
DROP TABLE IF EXISTS demo_items;
CREATE TABLE demo_items (
id SERIAL PRIMARY KEY,
embedding vector(3),
label TEXT
);
""")) {
stmt.execute();
}
// 3. 插入数据
String[][] vectors = {
{"[1,1,1]", "A"},
{"[2,2,2]", "B"},
{"[3,3,3]", "C"},
{"[10,10,10]", "D"}
};
for (String[] v : vectors) {
try (PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO demo_items (embedding, label) VALUES (?, ?)")) {
stmt.setString(1, v[0]);
stmt.setString(2, v[1]);
stmt.execute();
}
}
// 4. 创建 HNSW 索引
try (PreparedStatement stmt = conn.prepareStatement("""
CREATE INDEX IF NOT EXISTS idx_demo_items_hnsw
ON demo_items USING hnsw (embedding vector_l2_ops);
""")) {
stmt.execute();
}
// 5. 查询最近邻
String queryVector = "[2.5,2.5,2.5]";
try (PreparedStatement stmt = conn.prepareStatement("""
SELECT id, label, embedding,
embedding <-> ? AS distance
FROM demo_items
ORDER BY embedding <-> ?
LIMIT 3
""")) {
stmt.setString(1, queryVector);
stmt.setString(2, queryVector);
try (ResultSet rs = stmt.executeQuery()) {
System.out.println("查询向量: " + queryVector);
System.out.println("最近邻结果:");
while (rs.next()) {
String label = rs.getString("label");
double distance = rs.getDouble("distance");
System.out.printf(" label: %s, distance: %.4f%n", label, distance);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行说明:
mvn compile exec:java -Dexec.mainClass="com.example.Main"
多语言对比
| 维度 | Node.js | Go | Python | Java |
|---|---|---|---|---|
| 驱动/ORM | pg (node-postgres) | database/sql + lib/pq | psycopg2-binary | org.postgresql:postgresql (JDBC) |
| 连接方式 | 连接字符串 + Client 对象 | sql.Open + DSN | psycopg2.connect | DriverManager.getConnection |
| 参数占位符 | $1, $2, ... | $1, $2, ... | %s (或 %()s) | ? (但 PostgreSQL JDBC 也支持 $1,示例中使用了 ?) |
| 环境变量加载 | dotenv | godotenv | python-dotenv | dotenv-java |
| 错误处理 | try/catch + console.error | if err != nil + log.Fatal | try/except(示例中简化为直接检查) | try/catch + e.printStackTrace() |
| SQL 执行 | client.query() 直接返回结果 | db.Query() / db.Exec() | cur.execute() / cur.fetchall() | PreparedStatement.executeQuery() / execute() |
| 向量类型映射 | 字符串传递(PG 自动转换) | 字符串传递 | 字符串传递 | 字符串传递(或使用 PGobject) |
| 索引创建 | 同 SQL,使用 CREATE INDEX ... USING hnsw | 同左 | 同左 | 同左 |
| 依赖管理 | npm / package.json | go mod | pip / requirements.txt | Maven / pom.xml |
| 适用版本 | Node.js 16+ | Go 1.21+ | Python 3.8+ | Java 17+ (JDK 8+ 亦可) |
所有语言实现的功能完全一致,且均通过 PostgreSQL 原生 SQL 与 pgvector 扩展交互,不引入额外的 ORM 映射层,保持轻量和可控。开发者可根据自身技术栈选择对应的实现。
官方文档
- pgvector 官方文档:https://github.com/pgvector/pgvector
- pgvecto.rs 官方文档:https://docs.vectorchord.ai/
- PostgreSQL 扩展机制:https://www.postgresql.org/docs/current/extend.html
- postgresML 官方文档:https://postgresml.org/
参考链接
- PG生态向量扩展对比:https://github.com/pgvector/pgvector
- pgvecto.rs GitHub 仓库:https://github.com/tensorchord/pgvecto.rs
- VectorChord 官方文档:https://docs.vectorchord.ai/
- pgvector 维度限制讨论:https://github.com/pgvector/pgvector/issues/461
总结
PostgreSQL 生态围绕 AI 与向量检索已形成丰富的扩展矩阵:
- 向量存储与检索:以
pgvector为核心,提供vector数据类型、多种距离度量(L2、内积、余弦等)以及 HNSW/IVFFlat 索引。pgvector的vector类型最大支持 2000 维,halfvec可扩展至 4000 维。pgvecto.rs/VectorChord则将维度上限提升至 65535,并引入 VBASE 方法处理混合查询。 - 文本相似性:
pg_similarity提供非向量类的文本相似度计算,适用于模糊搜索与数据清洗。 - AI 应用层:
pg_summarize与pg_tokenize分别提供 LLM 摘要生成与分词能力,服务于 RAG 系统。 - 库内机器学习:
PGforML与postgresML支持在数据库内直接训练和推理 ML 模型。
在 PG 内统一管理向量数据与业务数据,可有效规避跨系统数据同步、一致性问题与额外运维成本,是构建 AI 应用的高效架构路径。
303



被折叠的 条评论
为什么被折叠?



