PostgreSQL笔记39:PG作为AI数据底座——向量检索与语义搜索能力解析

从零搭建AI应用的数据底座:PostgreSQL + PGVector向量数据库配置与连接实战 本文详细介绍了如何从零搭建AI应用的数据底座,重点讲解PostgreSQL与PGVector向量数据库的配置与连接实战。通过对比专用向量数据库的优缺点,展示PGVector方案在混合查询、事务一致性和部署复杂度上的独特优势,并提供生产级配置指南、Python生态集成及AI框架对接技巧,助力开发者高效构建智能应用。 阅读详情

纲要

  • 向量数据库与语义检索
    • 传统关系型数据库的语义检索短板
    • 向量数据库的核心能力:语义相似度搜索
  • PostgreSQL作为向量数据库的独特优势
    • 成熟的关系型数据库底座:ACID、索引、并发控制、备份恢复
    • 极强的可扩展性:自定义数据类型、操作符、索引访问方法、扩展插件
    • pgvector扩展:向量数据类型与相似度检索
  • pgvector扩展核心能力
    • 安装与启用
    • 向量数据类型:VECTOR、HALFVEC、SPARSEVEC
    • 距离操作符:L2距离(<->)、余弦距离(<=>)、负内积(<#>)、L1距离(<+>)
    • 近似最近邻索引:IVFFlat与HNSW
  • 混合查询:向量检索与传统SQL过滤、全文检索的结合
  • AI场景中的企业级能力:数据安全、高并发、监控告警
  • 行业趋势:PG生态在AI领域的战略价值

向量数据库与语义检索

在AI应用场景中,语义检索和相似度搜索正成为越来越普遍的需求。传统关系型数据库依赖关键词精确匹配,难以理解查询背后的语义含义。例如,当用户搜索“如何加快数据库查询速度”时,期望的结果是关于索引优化、参数调优、执行计划分析的文章,而非仅包含“查询速度”这一关键词的文档。这种语义层面的检索需求,正是传统关系型数据库难以胜任的。

向量数据库的核心任务,是在海量数据中高效存储向量表示,并快速找到与目标向量最相似的Top‑K结果。这类技术广泛应用于智能推荐、文本搜索、以图搜图等场景。然而,一个真正可用的向量数据库,其前提是首先必须是一个完整的数据库——具备高效的索引、过滤能力、多副本支持、数据更新删除、资源隔离、并发控制、查询接口、数据备份恢复、监控告警和限流等机制。向量检索只是冰山一角,底层的数据管理能力才是关键。

PostgreSQL作为向量数据库的独特优势

PostgreSQL本身就是一套成熟的关系型数据库,天然具备上述完整的数据管理能力。在此基础上,PG凭借其极强的可扩展性——允许自定义数据类型、操作符、索引访问方法以及各类扩展插件——使其能够通过pgvector等扩展自然地延伸到AI应用场景中。

可扩展性架构:

PostgreSQL 内核

自定义数据类型

自定义操作符

自定义索引访问方法

扩展插件机制

pgvector: VECTOR类型

pgvector: 距离操作符

pgvector: IVFFlat/HNSW索引

pgvector扩展

向量存储与检索能力

PostgreSQL的可扩展性体现在多个层面:

  • 自定义数据类型:通过CREATE TYPE定义新的数据类型
  • 自定义操作符:通过CREATE OPERATOR定义新的操作符
  • 自定义索引访问方法:通过CREATE ACCESS METHOD定义新的索引访问方法
  • 操作符类(Operator Class) :为新的数据类型定义索引所需的操作符类

pgvector正是利用了这一可扩展性架构,在PG的成熟底座上实现了向量数据类型的存储、距离操作符和近似最近邻索引。

相较于从零实现一套专用的向量数据库(如Milvus、Qdrant、Weaviate等),在PG之上叠加AI能力的方式更加简洁高效——既保留了关系型数据库的完整能力,又获得了向量检索和语义处理的能力。如无必要,勿增实体:一套简洁的架构可以减少数据孤岛,降低ETL数据搬运的额外成本和复杂度。

pgvector扩展核心能力

pgvector是一个开源的PostgreSQL扩展,为PG增加了向量存储和向量相似度搜索的能力。它支持精确最近邻搜索和近似最近邻搜索(ANN),支持单精度向量、半精度向量、二值向量和稀疏向量,并提供多种距离度量。

安装与启用

pgvector支持PostgreSQL 11及以上版本。安装完成后,在数据库中启用扩展:

CREATE EXTENSION IF NOT EXISTS vector;

如需指定版本(例如回退到0.7.4版本):

CREATE EXTENSION vector VERSION '0.7.4';

向量数据类型

pgvector提供了三种主要的向量类型:

数据类型描述适用场景
VECTOR(n)单精度浮点数向量,维度n(最大16000)通用向量嵌入,最常见
HALFVEC(n)半精度浮点数向量(16位),存储空间减半内存敏感场景,精度损失可接受
SPARSEVEC(n)稀疏向量,仅存储非零元素高维稀疏数据(如词袋、TF‑IDF)

创建包含向量列的表:

CREATE TABLE items (
    id BIGSERIAL PRIMARY KEY,
    embedding VECTOR(384),
    metadata JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

距离操作符

pgvector支持多种距离度量,对应不同的操作符:

距离度量操作符数学定义适用场景
欧几里得距离(L2)<-> ∑ ( a i − b i ) 2 \sqrt{\sum (a_i - b_i)^2} ∑(ai​−bi​)2 ​通用距离,数值差异敏感
余弦相似度(距离)<=> 1 − ∑ a i b i ∑ a i 2 ∑ b i 2 1 - \frac{\sum a_i b_i}{\sqrt{\sum a_i^2}\sqrt{\sum b_i^2}} 1−∑ai2​ ​∑bi2​ ​∑ai​bi​​语义相似度,方向敏感
负内积<#> − ∑ a i b i -\sum a_i b_i −∑ai​bi​最大化内积(推荐系统)
L1距离(曼哈顿)<+> ∑ ∣ a i − b i ∣ \sum |a_i - b_i| ∑∣ai​−bi​∣异常值鲁棒

精确最近邻查询示例:

-- 查询与目标向量最相似的5条记录(按L2距离排序)
SELECT id, metadata
FROM items
ORDER BY embedding <-> '[0.123, -0.456, 0.789, ...]'::VECTOR(384)
LIMIT 5;

-- 余弦距离查询
SELECT id, metadata
FROM items
ORDER BY embedding <=> '[0.123, -0.456, 0.789, ...]'::VECTOR(384)
LIMIT 5;

近似最近邻索引

对于大规模数据(百万级以上),精确扫描全表开销过高。pgvector提供了两种近似最近邻索引,以牺牲少量精度换取数量级的查询加速。

IVFFlat索引(基于倒排文件,支持lists参数):

-- 创建IVFFlat索引,必须先有数据并选择合适的聚类数(建议为 rows / 1000)
CREATE INDEX idx_items_embedding_ivfflat ON items
USING ivfflat (embedding VECTOR_L2_OPS)
WITH (lists = 100);

索引创建后,可通过SET ivfflat.probes = 10(默认1)调整探测列表数量,控制精度‑速度权衡。

HNSW索引(基于分层可导航小世界图,支持m和ef_construction参数):

-- 创建HNSW索引(pgvector 0.5.0+)
CREATE INDEX idx_items_embedding_hnsw ON items
USING hnsw (embedding VECTOR_L2_OPS)
WITH (m = 16, ef_construction = 200);

查询时可通过SET hnsw.ef_search = 40(默认40)控制动态候选列表大小。

索引类型对比:

特性IVFFlatHNSW
构建速度快较慢
查询速度较快(与probes相关)非常快
精度可调节高
内存占用低较高(图结构)
增量更新支持(需定期重建聚类)支持(动态插入)

版本要求:IVFFlat自pgvector 0.1.0起支持,HNSW自0.5.0起支持。推荐PostgreSQL 13+以获得更优的并行查询能力。

混合查询:向量检索与传统SQL过滤、全文检索的结合

pgvector的独特优势在于能够将向量相似度搜索与传统的SQL条件过滤、全文检索无缝结合,实现“先过滤后检索”或“先检索后过滤”等多种复合查询模式。

场景一:先过滤再检索(适用于过滤条件选择性高,减少向量搜索范围)

-- 查找特定类别下与目标向量最相似的物品
SELECT id, metadata
FROM items
WHERE metadata->>'category' = 'electronics'   -- 传统过滤
ORDER BY embedding <-> '[0.123, ...]'::VECTOR(384)
LIMIT 10;

场景二:先检索再过滤(适用于向量相似度为主要条件,过滤为次要)

-- 先按相似度搜索,再过滤出价格低于100的物品
WITH ranked AS (
    SELECT id, metadata, embedding <-> '[0.123, ...]'::VECTOR(384) AS dist
    FROM items
    ORDER BY dist
    LIMIT 50
)
SELECT id, metadata
FROM ranked
WHERE (metadata->>'price')::FLOAT < 100
ORDER BY dist;

场景三:向量检索结合全文检索(权重混合排序)

-- 同时考虑语义相似度和文本相关性,加权合并排序(需自定义评分)
SELECT
    id,
    metadata,
    (1 - (embedding <=> '[0.123, ...]'::VECTOR(384))) AS semantic_score,
    ts_rank(to_tsvector('english', metadata->>'description'), plainto_tsquery('query text')) AS text_score
FROM items
WHERE metadata->>'category' = 'books'
ORDER BY (0.7 * semantic_score + 0.3 * text_score) DESC
LIMIT 20;

PG的查询优化器能够智能选择索引(结合B‑Tree、GIN等传统索引和向量索引),并在并行查询、分区表等机制下高效执行混合负载。

AI场景中的企业级能力

AI应用中数据往往包含敏感信息,PG提供了完备的安全机制:

  • 透明数据加密(TDE) :数据在磁盘上加密存储(通过扩展或外部工具)
  • 精细权限控制:行级安全(RLS)、列级权限、基于角色的访问控制
  • 审计与合规:通过pgAudit等扩展记录操作日志
  • 备份与恢复:支持全量/增量备份、时间点恢复(PITR)

在资源管理方面,PG提供了连接池、语句超时、内存控制、I/O限流等机制,配合监控工具(如pg_stat_statements、Prometheus exporter)可构建生产级向量服务。

行业趋势:PG生态在AI领域的战略价值

近年来,Databricks收购Arcion(PG生态),Snowflake收购TruEra(AI评估),再如Databricks收购MosaicML(AI模型)等事件,均表明数据平台巨头正将PG生态作为AI智能体和应用的重要数据基础设施。PG凭借其深厚的关系型根基与向量扩展能力,成为兼具OLTP、OLAP、AI检索的统一数据平台,避免了数据孤岛和复杂ETL。

API 速览

以下梳理pgvector及相关PG核心API:

1. CREATE EXTENSION vector

  • 所属:PostgreSQL扩展管理
  • 签名:CREATE EXTENSION [IF NOT EXISTS] vector [VERSION 'version']
  • 说明:启用pgvector扩展,注册数据类型、操作符、索引访问方法。

2. VECTOR(n) 数据类型

  • 所属:pgvector
  • 签名:VECTOR(n),n为维度(正整数,≤16000)
  • 输入格式:'[x1, x2, ..., xn]' 或 '{x1, x2, ...}'
  • 示例:'[0.1, 0.2, 0.3]'::VECTOR(3)

3. 距离操作符

操作符签名返回类型示例
<->vector <-> vectorfloat8'[1,2]'::VECTOR(2) <-> '[3,4]'::VECTOR(2)
<=>vector <=> vectorfloat8同上
<#>vector <#> vectorfloat8同上
<+>vector <+> vectorfloat8同上

4. 索引访问方法 ivfflat 和 hnsw

  • 创建索引:CREATE INDEX ... USING ivfflat (column operator_class) WITH (lists = N)
  • 创建索引:CREATE INDEX ... USING hnsw (column operator_class) WITH (m = N, ef_construction = N)
  • 操作符类:vector_l2_ops(L2)、vector_cosine_ops(余弦)、vector_ip_ops(内积)
  • 运行时参数:SET ivfflat.probes = N;SET hnsw.ef_search = N

5. 辅助函数

  • vector_dims(vector):返回向量维度
  • vector_norm(vector):返回向量L2范数
  • l2_distance(vector, vector)、cosine_distance、inner_product 等函数,等价于操作符。

Demo 简单示例

以下是一个基于Node.js(使用pg客户端)的完整示例,演示了连接PG、创建扩展、建表、插入向量数据、创建索引和执行相似度查询。

项目结构:

demo/
├── package.json
├── index.js
└── .env (可选)

环境要求:

  • PostgreSQL 13+(推荐)
  • pgvector扩展已安装(需在数据库中执行CREATE EXTENSION)
  • Node.js 16+

package.json:

{
  "name": "pgvector-demo",
  "version": "1.0.0",
  "description": "PostgreSQL pgvector demo",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "pg": "^8.11.3"
  }
}

index.js(完整可运行示例):

const { Client } = require('pg');

const client = new Client({
  host: 'localhost',
  port: 5432,
  database: 'testdb',
  user: 'postgres',
  password: 'postgres'
});

async function run() {
  await client.connect();

  // 1. 启用扩展(如未启用)
  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. 插入示例数据(手动构造3维向量)
  const embeddings = [
    { vec: '[1, 1, 1]', label: 'A' },
    { vec: '[2, 2, 2]', label: 'B' },
    { vec: '[1, 2, 1]', label: 'C' },
    { vec: '[0, 0, 1]', label: 'D' },
    { vec: '[1, 0, 0]', label: 'E' },
  ];
  for (const item of embeddings) {
    await client.query(
      'INSERT INTO demo_items (embedding, label) VALUES ($1::VECTOR(3), $2)',
      [item.vec, item.label]
    );
  }

  // 4. 创建IVFFlat索引(需先有数据)
  await client.query(`
    CREATE INDEX IF NOT EXISTS idx_demo_ivfflat ON demo_items
    USING ivfflat (embedding VECTOR_L2_OPS)
    WITH (lists = 1);
  `);
  // 小数据量调整探测数
  await client.query('SET ivfflat.probes = 1');

  // 5. 精确相似度查询(按L2距离,返回最相似的2个)
  const target = '[1, 1, 0]';
  const res = await client.query(`
    SELECT id, label, embedding <-> $1::VECTOR(3) AS distance
    FROM demo_items
    ORDER BY distance
    LIMIT 2;
  `, [target]);

  console.log('Top 2 similar items (L2 distance to [1,1,0]):');
  res.rows.forEach(row => {
    console.log(`  ID: ${row.id}, Label: ${row.label}, Distance: ${row.distance}`);
  });

  // 6. 混合查询:过滤label为'A'或'C',再按相似度排序
  const filtered = await client.query(`
    SELECT id, label, embedding <-> $1::VECTOR(3) AS distance
    FROM demo_items
    WHERE label IN ('A', 'C')
    ORDER BY distance
    LIMIT 2;
  `, [target]);
  console.log('\nFiltered (label in A,C) top 2:');
  filtered.rows.forEach(row => {
    console.log(`  ID: ${row.id}, Label: ${row.label}, Distance: ${row.distance}`);
  });

  await client.end();
}

run().catch(err => {
  console.error(err);
  process.exit(1);
});

运行说明:

  1. 确保PostgreSQL运行中,且已创建数据库testdb(可自行修改连接配置)。
  2. 执行npm install安装依赖。
  3. 运行node index.js。

输出示例:

Top 2 similar items (L2 distance to [1,1,0]):
  ID: 1, Label: A, Distance: 1
  ID: 3, Label: C, Distance: 1

Filtered (label in A,C) top 2:
  ID: 1, Label: A, Distance: 1
  ID: 3, Label: C, Distance: 1

技术点总结:

  • 使用pg驱动执行原始SQL,演示pgvector的完整生命周期。
  • 展示精确最近邻查询和带过滤的混合查询。
  • 创建IVFFlat索引(最小lists数便于演示)。
  • 标注了注释对应的原生PG指令。

多语言示例

以下分别给出基于 Go、Python、Java 的完整 Demo,功能与前述 Node.js 示例完全一致:连接 PostgreSQL,启用 pgvector 扩展,创建含向量列的表,插入三维向量数据,创建 IVFFlat 索引,执行精确最近邻查询和带过滤的混合查询。

Go 示例

使用 pgx 驱动(支持 PostgreSQL 原生协议)。

项目结构:

go-demo/
├── go.mod
├── go.sum
└── main.go

go.mod:

module pgvector-go-demo

go 1.21

require github.com/jackc/pgx/v5 v5.5.0

main.go(完整可运行):

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/jackc/pgx/v5"
)

func main() {
    ctx := context.Background()

    // 1. 连接数据库
    conn, err := pgx.Connect(ctx, "postgres://postgres:postgres@localhost:5432/testdb")
    if err != nil {
        log.Fatal("连接失败:", err)
    }
    defer conn.Close(ctx)

    // 2. 启用扩展
    _, err = conn.Exec(ctx, "CREATE EXTENSION IF NOT EXISTS vector")
    if err != nil {
        log.Fatal("启用扩展失败:", err)
    }

    // 3. 创建测试表
    _, err = conn.Exec(ctx, `
        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)
    }

    // 4. 插入示例数据
    items := []struct {
        vec   string
        label string
    }{
        {"[1,1,1]", "A"},
        {"[2,2,2]", "B"},
        {"[1,2,1]", "C"},
        {"[0,0,1]", "D"},
        {"[1,0,0]", "E"},
    }
    for _, it := range items {
        _, err = conn.Exec(ctx,
            "INSERT INTO demo_items (embedding, label) VALUES ($1::VECTOR(3), $2)",
            it.vec, it.label,
        )
        if err != nil {
            log.Fatal("插入失败:", err)
        }
    }

    // 5. 创建IVFFlat索引(需先有数据)
    _, err = conn.Exec(ctx, `
        CREATE INDEX IF NOT EXISTS idx_demo_ivfflat ON demo_items
        USING ivfflat (embedding VECTOR_L2_OPS)
        WITH (lists = 1);
    `)
    if err != nil {
        log.Fatal("创建索引失败:", err)
    }
    // 设置探测数
    _, err = conn.Exec(ctx, "SET ivfflat.probes = 1")
    if err != nil {
        log.Fatal("设置参数失败:", err)
    }

    target := "[1,1,0]"

    // 6. 精确查询(Top 2)
    rows, err := conn.Query(ctx,
        `SELECT id, label, embedding <-> $1::VECTOR(3) AS distance
         FROM demo_items
         ORDER BY distance
         LIMIT 2;`,
        target,
    )
    if err != nil {
        log.Fatal("查询失败:", err)
    }
    fmt.Println("Top 2 similar items (L2 distance to [1,1,0]):")
    for rows.Next() {
        var id int
        var label string
        var dist float64
        if err := rows.Scan(&id, &label, &dist); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("  ID: %d, Label: %s, Distance: %v\n", id, label, dist)
    }
    rows.Close()

    // 7. 混合查询(过滤 label IN ('A','C'))
    rows, err = conn.Query(ctx,
        `SELECT id, label, embedding <-> $1::VECTOR(3) AS distance
         FROM demo_items
         WHERE label IN ('A', 'C')
         ORDER BY distance
         LIMIT 2;`,
        target,
    )
    if err != nil {
        log.Fatal("混合查询失败:", err)
    }
    fmt.Println("\nFiltered (label in A,C) top 2:")
    for rows.Next() {
        var id int
        var label string
        var dist float64
        if err := rows.Scan(&id, &label, &dist); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("  ID: %d, Label: %s, Distance: %v\n", id, label, dist)
    }
    rows.Close()
}

运行说明:

  1. 确保 PostgreSQL 运行,数据库 testdb 已创建。
  2. 执行 go mod tidy 拉取依赖。
  3. 运行 go run main.go。

Python 示例

使用 psycopg2(PostgreSQL 适配器,支持 pgvector)。

项目结构:

python-demo/
├── requirements.txt
└── main.py

requirements.txt:

psycopg2-binary>=2.9.9

main.py(完整可运行):

import psycopg2

def main():
    # 1. 连接数据库
    conn = psycopg2.connect(
        host="localhost",
        port=5432,
        dbname="testdb",
        user="postgres",
        password="postgres"
    )
    cur = conn.cursor()

    # 2. 启用扩展
    cur.execute("CREATE EXTENSION IF NOT EXISTS vector")

    # 3. 创建测试表
    cur.execute("""
        DROP TABLE IF EXISTS demo_items;
        CREATE TABLE demo_items (
            id SERIAL PRIMARY KEY,
            embedding VECTOR(3),
            label TEXT
        );
    """)

    # 4. 插入示例数据
    items = [
        ("[1,1,1]", "A"),
        ("[2,2,2]", "B"),
        ("[1,2,1]", "C"),
        ("[0,0,1]", "D"),
        ("[1,0,0]", "E"),
    ]
    for vec, label in items:
        cur.execute(
            "INSERT INTO demo_items (embedding, label) VALUES (%s::VECTOR(3), %s)",
            (vec, label)
        )

    # 5. 创建IVFFlat索引
    cur.execute("""
        CREATE INDEX IF NOT EXISTS idx_demo_ivfflat ON demo_items
        USING ivfflat (embedding VECTOR_L2_OPS)
        WITH (lists = 1);
    """)
    cur.execute("SET ivfflat.probes = 1")

    target = "[1,1,0]"

    # 6. 精确查询
    cur.execute("""
        SELECT id, label, embedding <-> %s::VECTOR(3) AS distance
        FROM demo_items
        ORDER BY distance
        LIMIT 2;
    """, (target,))
    print("Top 2 similar items (L2 distance to [1,1,0]):")
    for id, label, dist in cur.fetchall():
        print(f"  ID: {id}, Label: {label}, Distance: {dist}")

    # 7. 混合查询
    cur.execute("""
        SELECT id, label, embedding <-> %s::VECTOR(3) AS distance
        FROM demo_items
        WHERE label IN ('A', 'C')
        ORDER BY distance
        LIMIT 2;
    """, (target,))
    print("\nFiltered (label in A,C) top 2:")
    for id, label, dist in cur.fetchall():
        print(f"  ID: {id}, Label: {label}, Distance: {dist}")

    cur.close()
    conn.commit()
    conn.close()

if __name__ == "__main__":
    main()

运行说明:

  1. 确保 PostgreSQL 运行,数据库 testdb 已创建。
  2. 执行 pip install -r requirements.txt。
  3. 运行 python main.py。

Java 示例

使用 JDBC(PostgreSQL 驱动)。

项目结构:

java-demo/
├── pom.xml
└── src/main/java/PgVectorDemo.java

pom.xml:

<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>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.1</version>
        </dependency>
    </dependencies>
</project>

src/main/java/PgVectorDemo.java:

import java.sql.*;

public class PgVectorDemo {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/testdb";
        String user = "postgres";
        String password = "postgres";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             Statement stmt = conn.createStatement()) {

            // 1. 启用扩展
            stmt.execute("CREATE EXTENSION IF NOT EXISTS vector");

            // 2. 创建测试表
            stmt.execute("DROP TABLE IF EXISTS demo_items");
            stmt.execute("""
                CREATE TABLE demo_items (
                    id SERIAL PRIMARY KEY,
                    embedding VECTOR(3),
                    label TEXT
                );
            """);

            // 3. 插入示例数据
            String[][] items = {
                {"[1,1,1]", "A"},
                {"[2,2,2]", "B"},
                {"[1,2,1]", "C"},
                {"[0,0,1]", "D"},
                {"[1,0,0]", "E"}
            };
            try (PreparedStatement pstmt = conn.prepareStatement(
                    "INSERT INTO demo_items (embedding, label) VALUES (?::VECTOR(3), ?)")) {
                for (String[] item : items) {
                    pstmt.setString(1, item[0]);
                    pstmt.setString(2, item[1]);
                    pstmt.executeUpdate();
                }
            }

            // 4. 创建IVFFlat索引
            stmt.execute("""
                CREATE INDEX IF NOT EXISTS idx_demo_ivfflat ON demo_items
                USING ivfflat (embedding VECTOR_L2_OPS)
                WITH (lists = 1);
            """);
            stmt.execute("SET ivfflat.probes = 1");

            String target = "[1,1,0]";

            // 5. 精确查询
            try (PreparedStatement pstmt = conn.prepareStatement("""
                    SELECT id, label, embedding <-> ?::VECTOR(3) AS distance
                    FROM demo_items
                    ORDER BY distance
                    LIMIT 2;
                """)) {
                pstmt.setString(1, target);
                ResultSet rs = pstmt.executeQuery();
                System.out.println("Top 2 similar items (L2 distance to [1,1,0]):");
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String label = rs.getString("label");
                    double dist = rs.getDouble("distance");
                    System.out.printf("  ID: %d, Label: %s, Distance: %f%n", id, label, dist);
                }
            }

            // 6. 混合查询
            try (PreparedStatement pstmt = conn.prepareStatement("""
                    SELECT id, label, embedding <-> ?::VECTOR(3) AS distance
                    FROM demo_items
                    WHERE label IN ('A', 'C')
                    ORDER BY distance
                    LIMIT 2;
                """)) {
                pstmt.setString(1, target);
                ResultSet rs = pstmt.executeQuery();
                System.out.println("\nFiltered (label in A,C) top 2:");
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String label = rs.getString("label");
                    double dist = rs.getDouble("distance");
                    System.out.printf("  ID: %d, Label: %s, Distance: %f%n", id, label, dist);
                }
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行说明:

  1. 确保 PostgreSQL 运行,数据库 testdb 已创建。
  2. 执行 mvn compile 编译。
  3. 执行 mvn exec:java -Dexec.mainClass="PgVectorDemo" 或直接运行 Java 类(需将 PG JDBC 驱动置于 classpath)。

多语言对比

维度Node.js (pg)Go (pgx)Python (psycopg2)Java (JDBC)
驱动/库pggithub.com/jackc/pgx/v5psycopg2org.postgresql:postgresql
连接方式new Client() 连接字符串pgx.Connect()psycopg2.connect()DriverManager.getConnection()
参数占位符$1, $2, ...$1, $2, ...%s 或 $1(使用execute)? (PreparedStatement)
向量类型转换直接传递字符串,用 ::VECTOR(n) 显式转换同上同上同上
异步支持原生 async/await原生 context + 协程同步(另有 asyncpg 支持异步)同步(JDBC)
依赖大小~数 MB~数 MB~数 MB~数 MB(Maven)
典型适用场景快速原型、全栈 JS高并发微服务数据科学、脚本企业级应用
错误处理try/catcherror 返回值try/exceptSQLException

所有示例均使用相同版本的 PostgreSQL 和 pgvector 扩展,执行逻辑完全等价,输出结果一致。开发者可根据自身技术栈选择对应语言,并结合官方驱动文档进行生产级扩展(如连接池、超时控制、重试等)。

项目难点与解决方案

  • 核心难点:将AI向量检索能力无缝集成到传统关系型数据库中,同时保持ACID、多租户隔离和备份恢复等企业级特性。
  • 解决方案:借助PG的可扩展机制,通过pgvector插件注入向量类型、操作符和索引,无需修改内核。利用已有的分区、并行查询、连接池等基础设施支撑高并发混合负载。
  • 广度:覆盖了从底层数据类型、操作符、索引到上层混合查询、安全、监控的全链路。
  • 深度:深入对比了IVFFlat和HNSW的适用场景、参数调优和精度‑性能权衡,并给出了实际代码示例。
  • 复杂度:涉及PG扩展开发原理、查询优化器与向量索引的协作机制,以及生产环境部署中的参数调优(如maintenance_work_mem、max_parallel_workers等)。

官方文档

参考链接

总结

PostgreSQL依托其成熟的关系型数据库底座、强大的可扩展性以及pgvector扩展,在AI应用场景中展现出独特的竞争力。它不仅是传统的OLTP/OLAP引擎,更能直接支持向量存储、相似度搜索和混合查询,从而避免引入专用向量数据库带来的架构复杂性和数据搬运成本。

本文详细剖析了pgvector的数据类型、距离操作符、索引类型(IVFFlat/HNSW)及其适用版本,给出了完整的混合查询示例和Node.js可运行Demo,并探讨了企业级安全、监控等关键能力。在AI驱动的下一代数据基础设施中,PG凭借其生态整合能力,正成为越来越多企业的首选数据底座。

从LangChain到Agent中台:构建可治理、可复用的AI能力中枢 Agent(智能体)作为大模型落地的核心范式,正从单点Demo走向规模化生产。其本质是基于状态机与工具调用的自主决策系统,依赖LangGraph实现流程编排、LangSmith保障可观测性,并通过中台化架构解决多业务线协同中的权限隔离、资源调度与生命周期管理难题。技术价值在于将AI能力抽象为水电般的标准化服务,支撑RAG、工具调用、多轮对话等典型场景。本文聚焦工程化落地路径,详解如何以Tool注册网关、PostgreSQL状态存储、ABAC动态鉴权和LangSmith深度集成为抓手,构建稳定、可观测、易治理 阅读详情

相关推荐

Grok-4实战能力图谱:7个业务断点验证推理深度与上下文处理

大语言模型的推理能力并非抽象概念,而是体现在对真实业务场景中‘认知摩擦点’的解决效能上。其核心原理在于链式思维(Chain-of-Thought)的稳定性、多源信息的语义对齐能力,以及对用户意图的动态澄清机制。技术价值在于将模糊需求转化为可执行规格、跨文档事实自动比对、长文本结构化理解等高阶认知任务,显著降低人工研判成本。典型应用场景包括法务合同风险识别、实时财报动因分析、SRE故障闭环排查、B端需求工程转化等——这些正是Prompt Engineering、Reasoning Depth与Context

weixin_30859423的博客 466

Supabase 架构与生态全解:以 PostgreSQL 为核心的开源后端开发平台

Supabase 是一个定位为 “The Postgres development platform” 的开源项目,也就是把云原生 API、认证、实时订阅、文件存储、边缘函数等后端能力全部「长」在一颗专用 PostgreSQL 数据库之上。本文以仓库中[西语版 README(i18n/README.es.md)](https://link.gitcode.com/i/237e687477949d9

gitblog_00940的博客 218

【合规领域】【信息科学与工程学】计算机科学与自动化——第五百篇 国产化/XC解决方案 第二篇 海光CPU/DPU芯片适配01

信创软件与海光CPU/DCU(深算单元,常被统称为海光GPU加速卡)的适配,核心思路是:海光CPU兼容x86指令集、海光DCU兼容"类CUDA"环境,因此绝大多数x86应用和CUDA生态组件可以通过"轻量迁移"而非"重写"的方式落地。本条目的目标是把"海光x86 CPU + 海光DCU加速卡 + 统信UOS/银河麒麟 + 国密算法"串成一条可验收的链路。海光CPU(C86系列):兼容x86指令集(完整x86-64及SSE、AVX等主流扩展),基于AMD Zen1微架构深度定制的国产路线,国内唯一获得x86指

weixin_49199313的博客 116

Supabase 深度解读:基于 Postgres 与开源组件打造的 Firebase 式后端开发平台

本文以仓库内 [i18n/README.pt.md](https://link.gitcode.com/i/65b8be90e8414c4a9b12d45fddab7b32)(葡萄牙语版项目自述)为核心脉络,结合当前仓库中 `apps/`、`packages/`、`docker/` 的真实实现,系统拆解 Supabase 的平台定位、核心功能矩阵、分层架构与客户端生态。读完本文,你将能说清 Sup

gitblog_00405的博客 496

【信息科学与工程学】【产品体系】第二十七篇 存储系统系列一 01

存储系统是一个横跨单机引擎 → 分布式系统 → 硬件指令集 → 云上多地域的四层技术栈。下面按你给的格式,用一组相互关联的表把"各类特性组合"一次性铺开。版本号以各厂商当前 GA 版本为准,功能清单为生产级能力摘要。编号存储系统(厂商 / 内核 / 引擎 / 版本 / 功能清单)S01Ceph(Red Hat / 自研内核模块 / RADOS + BlueStore + RocksDB / Squid 19.x)/ 功能:RBD 块、RGW 对象(S3/Swift)、CephFS POSIX、副本+EC 混

weixin_49199313的博客 367

Vanna SQL生成框架实战:解决数据库连接与LLM集成核心问题

SQL生成是自然语言转数据库查询的关键技术,其本质依赖于元数据感知、LLM推理与数据库驱动协同。Vanna作为LangChain增强型SQL生成框架,通过DDL注入、意图澄清和语法预检等机制,显著提升生成SQL的可用性与可维护性。其技术价值体现在降低数据分析门槛、加速BI接口开发,并支持本地Ollama与远程OpenAI双模LLM接入。典型应用场景包括业务取数、SQL审计、慢查询优化及新手数据库实践。本文聚焦Vanna落地中最常见的数据库连接失败问题——涵盖SQLite、PostgreSQL、SQL Ser

weixin_29597551的博客 69

AI应急呼叫系统:从语音接入到工单调度的全链路实现

实时语音交互与智能客服系统正在从简单问答升级为复杂事件响应平台。基于ASR语音转写、LLM意图理解、地图定位与工单调度,可以构建一条从呼叫接入到事件分发的实时链路。这类系统在应急热线、社区告警、企业服务台等场景中,能显著降低人工信息录入负担,提升事件响应效率。其核心技术价值在于将多模态信息结构化,并确保低延迟、可回溯、可人工接管。以AI应急呼叫系统为案例,介绍其核心模块、系统架构、部署示例、测试方法与API设计,为智能呼叫中心和应急指挥类项目提供工程参考。

weixin_33836874的博客 491

Win10环境借助DockerDesktop部署Open web UI集成DeepSeek

Win10环境借助DockerDesktop部署Open web UI集成DeepSeek,并借助DuckDuckGo实现联网查询

虎鲸不是鱼 1705

【独家整理】从零学会 MagicDraw:中文培训教程指南

打开 MagicDraw,满屏英文菜单,连"新建 SysML 模型"的入口都找不到;啃完英文文档,术语还是对不上号。这份 MagicDraw 中文培训教程就是为这种处境准备的:用中文、按步骤,带你从零走到第一张图。 ## 它到底能帮你做什么 这套教程由 5 份 PPT 和 1 份 PDF 组成(打包在 CATIA Magic培训材料.zip),顺序覆盖了从认识软件到实际开工的路径。说白了,它主

gitblog_00219的博客 484

生成式引擎优化(GEO)全流程解析

生成式引擎优化(GEO)已成为AI时代数字营销新趋势,通过优化企业内容以提升在AI生成答案中的引用率。本文系统梳理了GEO优化的完整流程,包括:1)前期准备与市场调研;2)数据治理与知识库构建;3)模型适配与算法优化;4)内容权威性建设;5)本地化营销;6)效果监测与优化。研究显示,实施GEO的企业AI曝光率提升62%,转化率增长2.3倍。未来GEO将向多模态内容、实时个性化、垂直场景深化方向发展。企业需建立动态知识图谱,采用结构化数据标记,持续优化内容策略,以在AI搜索时代获取流量优势。

解锁AI流量新红利!🚀 GEO优化(生成引擎优化)助您掌控AI搜索,让AI成为您的品牌最佳代言人! 3255

GEOS-Chem笔记——模式下载安装+驱动数据+运行流程

看过我博客的会发现我写了很多CESM的笔记,但是说实话,平时科研我其实更多时候是GEOS-Chemuser。之所以写了很多CESM笔记,其实是因为不会,就边学边写。然后GEOS-Chem已经用比较熟了,所以也没想过做笔记啥的。但是我发现越来越多朋友私信我各种CESM问题,我觉得我可能给大家营造了一种我是CESM达人的错觉。感觉自己有点跑偏了呢。公平起见,我觉得我有必要补充点GEOS-Chem笔记。 GEOS-Chem是研究大气化学的人常用的大气化学传输模式,在全世界范围的大气化学研究组中被广泛使用。不过.

砍柴姑娘Jourosy的博客 1万+

10 分钟打通 BepInEx IL2CPP:Unity IL2CPP 插件加载、验收与排障指南

装完启动,游戏闪退或日志里一个插件都没出现,是 IL2CPP 后端最高频的卡点。本文把 Unity 插件加载链路拆成:判断后端 → 安装放置 → 校准 Doorstop 配置 → 首启验收 → 定点排障,五步走完。完成标志很具体:`BepInEx/LogOutput.log` 里插件的 `Loading` 行计数大于 0 且与 `plugins` 目录内的插件数一致,没有针对你插件的 `Skipp

gitblog_00793的博客 106

【STM32】IEC60730 Class B软件安全实现 Part2:自检库实战指南

本文是STM32 IEC60730 Class B软件安全实现的实战指南,详细介绍了如何将X-CUBE-CLASSB自检库集成到用户项目中。内容涵盖软件包解构、内存划分、启动与运行时自检的集成步骤、关键代码示例及调试技巧,并阐述了为最终通过安全认证所需的文档与流程准备,帮助开发者高效实现MCU功能安全。

weixin_29185167的博客 92

Mac下MySQL的配置和修改密码以及忘记密码后的重置密码

1 ) Mac下MySQL的配置和修改密码 2 ) 忘记并重置密码 启动MySQL服务,通过 $ps aux | grep mysql 判断是否启动该服务,若未启动,则到 系统偏好设置/MySQL面板 启动服务 软件默认会安装到:/usr/local/mysql 目录,进入这个目录下的bin目录:$cd /usr/local/mysql/bin 执行 $./mysql -u root -p 通过初始密码登录MySQL 通...

Wang的专栏 6259

发那科机器人弧焊指令实战:从Search指令到组掩码设置的完整避坑指南

本文详细解析了发那科机器人弧焊指令系统的实战应用,从Search指令的正确使用到组掩码设置的避坑技巧。通过实际案例和参数优化建议,帮助工程师提升焊接质量和生产效率,避免常见错误操作。特别针对T2模式示教难题和外部轴协调运动提供了专业解决方案。

weixin_29040367的博客 185

【EB-03】 AUTOSAR builder与EB RTE集成

打开EB工具后,在EB的步骤是:执行MultiTask(2)-> Mapping Component to Partitions(4)->Task Mapping(5)-> SvcAs_Trigger(7)-> Code Generation(8)如涉及新增BSW Module,也需要重复以上步骤。

libuping2023的博客 965

mysql数据库的一些简单的sql语句 内连接 - 表的关联查询

# 创建一个user表 create table users( id int primary key auto_increment, name varchar(20), dpt_id int ); # 向user表中插入数据 insert into users values (null,'刘备',1), (null,'关羽',2), (null,'赵

Wang的专栏 4264

50、压电微泵与技术设备驱动的研究分析

本文研究了压电微泵在弹性微通道中的流体流动特性及技术设备驱动的振动行为。通过建立轴对称流固耦合模型,利用FreeFem++进行数值模拟,分析了不同数量压电致动器和频率对微泵流量的影响,发现多个致动器按特定时序压缩可产生净正向流量,并提出了稳态流量的近似解析公式。同时,针对技术设备驱动系统,探讨了周期性负载引起的强迫振动特性,明确了共振条件及影响因素,并比较了主动、被动和优化设计等振动控制策略。最后展望了实验验证、多物理场耦合与智能控制等未来研究方向,为相关设备的优化设计与高效运行提供了理论支持。

convnet3designer的博客 148

数据库、数据库类型、RDBMS以及MySQL

什么是数据库 数据库 就是按一定的结构来组织、存储和管理数据的仓库 可以很方便的进行crud, 能提供多个用户共享、具有尽可能小的冗余度、与应用程序批次独立的数据集合。 其他持久储存缺点:文件操作(不灵活) 数据库类型 包括 : 关系型 和 非关系型 【关系型】 MySQL MariaDB PostgreSQL Microsoft SQL Server Oracle数据库 Sybase 【...

Wang的专栏 4039

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Eggjs笔记:Eggjs集成ElasticSearch及CRUD操作

相关文档 https://www.elastic.co/guide/index.html https://www.elastic.co/guide/en/elasticsearch/client https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/configuration.html https://www.elastic.co/guide/en/elasticsearch/client/javascrip

Wang的专栏 3530

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Redis: 集群高可用之故障转移和集群迁移

若要检查源和目标是否数据统一,还可选择阿里配套工具RedisFullCheck。3.4 使用 RedisFullCheck 工具检查。下载官方编译好的工具包,直接解压后就能使用。故障转移,包括自动故障转移和手动故障转移。运行之后,就给一个反馈结果,如果是。1.2 原集群与目标集群结构不一致。1.1 原集群与目标集群结构一致。2 ) 手动故障转移。

Wang的专栏 2918

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Python笔记:基于Django框架的数据库设计

概述 需要根据《项目建议书》、《需求说明书》、界面原型、各种标准规范对数据库进行设计, 我们目前先参考这一设计图 结构设计 1 ) 找实体 实体是实体-关系模型的基本对象, 是现实世界中各种事物的抽象。 凡是可以相互区 开并可以被识 的事、物、概念等对象均可认为是实体。 基本的实体列表如下: 会员 类别 商品 订单 订单详情 收货地址 商品评论 商品图片 友情链接 站内公告 … ...

Wang的专栏 2772

Elastic Stack梳理:集群搭建指南之高可用架构与实战部署

本文介绍了Elasticsearch分布式架构和Cerebro监控工具的部署策略。主要内容包括: ES集群角色划分(主节点、数据节点、协调节点)及功能说明 Cerebro监控工具的独立部署方法和访问方式 集群拓扑规划示例,包含节点IP分配和架构拓扑图 各类型节点的配置原则和数量计算公式 高可用主节点配置要点及示例配置 生产环境优化建议,包括分片规划、硬件配置和安全措施 文章提供了完整的集群部署方案,涵盖从基础概念到具体实施的各个环节,适用于不同规模的ES集群建设。

Wang的专栏 2545

Redis: 集群环境搭建,集群状态检查,分析主从日志,查看集群信息

当从节点获得足够多的选票时,它就会成为新的主节点,并增加自己的configEpoch来更新槽位配置。在Redis集群中,纪元的更新通常是由节点间的通信和状态变更触发的。例如,当某个节点接收到来自其他节点的状态变更通知时,它会比较通知中的纪元与自己的纪元。如果通知中的纪元更大,则更新自己的纪元。确保集群状态的一致性:通过递增的纪元,集群中的节点可以确保它们对集群状态的认知是一致的。通过递增的纪元版本号,集群中的节点可以确保它们对集群状态的认知是一致的,并解决可能出现的配置冲突问题。4 )纪元的更新机制。

Wang的专栏 2466

MySQL: 数据库性能的决定性因素之从双十一大促高并发场景看系统瓶颈与优化路径

电商大促期间,数据库面临高并发写入、连接数暴增、慢查询积压等核心挑战。本文分析了锁竞争、主从延迟等关键性能瓶颈,并提出多种优化方案:1)使用乐观锁通过版本号控制库存超卖;2)CAS机制实现无锁并发;3)分布式锁协调多节点操作。这些方案在保证数据一致性的同时提升吞吐量,适用于库存扣减、订单创建等典型场景,有效解决数据库扩展难题。

Wang的专栏 2426
上一篇: PostgreSQL笔记38:慢查询定位与优化的系统方法论
下一篇: PostgreSQL笔记40:PG生态中AI与向量检索扩展全景概览
Wang's Blog
博客等级 码龄12年 7438粉丝 2422原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值