SEO技术

SEO技术

Products

当前位置:首页 > SEO技术 >

如何高效在PostgreSQL中实现向量搜索?

96SEO 2026-08-03 11:16 15


说起来,

做 Agent 项目。向量检索几乎是绕不开的一环。

RAG 知识库、对话记忆、工具调用前的上下文召回——流程都差不多:文本转成 Embedding,存起来查询时按相似度找最相关的内容。

如何向量搜索?

常见痛点

  • 双写复杂度高:业务库和向量库需要保持同步,出现写入失败或 ID 映射错误时数据容易不一致。
  • 跨库查询性能差:先在向量库召回 ID。再去关系库补全详情,多一次网络往返导致响应慢。
  • 运维成本大:维护两套数据库需要额外的监控、备份与扩容策略。
  • 过滤与权限控制困难:向量库通常只提供相似度搜索,业务层面的使用者、会话、权限过滤只能在应用层二次处理。

技术选型对比

PostgreSQL + pgvector

  • 降低架构复杂度,一个库同时管业务和向量。
  • 写入走同一事务。强一致性,无需维护双写或 ID 映射。
  • 检索时可以直接在 SQL 中加入业务过滤,一次查询搞定。
  • 适合向量规模在百万~千万级的快速落地项目。

MySQL + Milvus

  • 业务已经深度绑定 MySQL,不想改动主库结构。
  • 向量规模千万级以上或 QPS 很高时Milvus 的专用索引更具优势。
  • 支持语义 + 关键词混合检索,满足更复杂的搜索需求。
  • 接受双写、跨库协调还有额外的运维工作。

先把 PostgreSQL 跑起来

本地采用 Docker Compose 启动 PostgreSQL和 pgAdmin,并通过初始化脚本自动建表。

services:
# PostgreSQL with pgvector
postgres:
# 官方 pgvector 镜像,基于 PostgreSQL,内置向量
说到image,pgvector/pgvector:pg16
container_name: pg_vector_db
restart: always
# 环境变量
environment:
POSTGRES_USER: copyer
POSTGRES_PASSWORD:
POSTGRES_DB: copyer-test
从ports来看,- "5432:5432"
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/postgres:/var/lib/postgresql/data
# 初始化脚本
- ./init-scripts:/docker-entrypoint-initdb.d
healthcheck:
说到test。interval: 5s
timeout: 5s
retries: 5
# PostgreSQL GUI
pgAdmin:
container_name: pgAdmin
image这方面,dpage/pgadmin4:latest
environment:
PGADMIN_DEFAULT_EMAIL:
PGADMIN_DEFAULT_PASSWORD: copyer
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/pgadmin:/var/lib/pgadmin
healthcheck:
test这方面,interval: 30s
timeout: 20s
retries: 5
ports的观点是,- "8080:80"
depends_on:
- postgres
networks:
default:
再看name,common-network

从关键配置说明来看,

  • 镜像:直接使用官方带 pgvector 的镜像,无需手动装插件。话说回来,
  • 环境变量:初始化使用者名、密码、数据库名。可放 .env 管理,
  • 数据挂载:持久化容器数据,即使删除容器也能保留数据。话说回来,
  • 初始化脚本:第一次启动自动执行目录下的 SQL。用于建表和
  • 端口映射 & 健康检查:确保外部能够访问并监控容器状态。不过,
  • pgAdmin:

Create table script

在 /init-scripts/create-story.sql 中添加下面内容:

-- 启用 pgvector
CREATE EXTENSION IF NOT EXISTS vector;老实说,-- 小说 chunk 向量表
CREATE TABLE IF NOT EXISTS story (
id VARCHAR PRIMARY KEY,vector VECTOR NOT NULL,book_id VARCHAR NOT NULL。book_name VARCHAR NOT NULL,chapter_num INTEGER NOT NULL,"index" INTEGER NOT NULL,content TEXT NOT NULL
);-- 按书籍过滤加速
CREATE INDEX IF NOT EXISTS idx_story_book_id ON story;其实,-- 向量相似度检索
CREATE INDEX IF NOT EXISTS idx_story_vector_cosine
ON story USING hnsw;

启动容器这方面,

docker-compose up -d

PgAdmin 登录后即可看到 copyer-test 数据库和 story` 表已创建完毕。

最小验证的观点是,写入与检索

A) 安装依赖:

pnpm add pg dotenv @langchain/openai # pg 用于连接 PostgreSQL,dotenv 加载环境变量。@langchain/openai 调用 embedding 接口

A1. 插入示例数据并生成向量

// src/insert.mjs
import { config } from "dotenv";import pg from "pg";import { OpenAIEmbeddings } from "@langchain/openai";config,const pool = new pg.Pool({
说到host,process.env.PG_HOST?,"localhost",port: Number。user: process.env.PG_USER?,"copyer",password: process.env.PG_PASSWORD?,"",database: process.env.PG_DATABASE?,"copyer-test",});const embeddings = new OpenAIEmbeddings({
再看model,process.env.AI_EMBEDDING_MODEL?,"text-embedding-v3",dimensions: Number,apiKey: process.env.AI_EMBEDDING_KEY。configuration: { baseURL : process.env.AI_EMBEDDING_BASE_URL },});function toVectorLiteral {
return ``;}
// 模拟文本样例
const samples =;for {
const vector = await embeddings.embedQuery;老实说,await pool.query(
`INSERT INTO story
VALUES
ON CONFLICT DO UPDATE SET vector = EXCLUDED.vector。content = EXCLUDED.content`,);}
await pool.end;

A2. 检索示例

// src/query.mjs
import { config } from "dotenv";import pg from "pg";import { OpenAIEmbeddings } from "@langchain/openai";config,const pool = new pg.Pool;const embeddings = new OpenAIEmbeddings;function toVectorLiteral {
return ``;}
const question = "PostgreSQL 怎么做向量检索?",const queryVector = await embeddings.embedQuery;const { rows } = await pool.query(
`SELECT id,content。- AS score
FROM story
WHERE book_id = $2
ORDER BY vector <=> $1::vector
LIMIT 5`,);
rows.forEach=> console.log.toFixed} → ${r.content}`));await pool.end;

This demonstrates core of **single‑library** approach:

  • Simplified write: INSERT …ON CONFLICT …
  • Simplified read: SQ L ORDER BY vector <=>,no extra service layer.

当 Agent 需要「记住聊天」

User‑centric conversation memory 常见痛点包括:

  • ID 映射繁琐:
  • L​atency 升高: 两次跨库请求导致整体响应时间增加。其实,
  • 权限过滤难实现: 向量库缺少细粒度的行级安全。只能在业务层二次过滤,

P​ostgreSQL + pgvector 把「消息内容」和「向量」放同一张表,实现“一条 SQL 完成过滤 + 相似度排序”。下面展示基于 NestJS + Prisma 的完整实现思路。

NestJS 项目初始化与 Prisma 配置

 nest new nestjs-pg --package-manager pnpm --skip-git 
 pnpm add @nestjs/config @prisma/client class-validator class-transformer 
 pnpm add -D prisma 
pnpm prisma init 

.env 示例:

 DATABASE_URL=postgresql://copyer:@localhost/:5432/copyer-test?schema=public 

Prisma Schema – 三张主要表

 generator client {
provider = "prisma-client-js"
previewFeatures =
}
datasource db {
provider = "postgresql"
url = env
extensions =
}
model User {
id Int @id @default)
name String
createdAt DateTime @default) @map
conversations Conversation
@@map
}
model Conversation {
id Int @id @default)
userId Int @map
title String?createdAt DateTime @default) @map
user User @relation
messages Message
@@map
}
model Message {
id Int @id @default)
conversationId Int @map
role String // user / assistant / system
content String
vector Unsupported")?// 存储 embedding
createdAt DateTime @default) @map
conversation Conversation @relation
@@map
} 

Migrate 并同步到数据库:

 pnpm prisma migrate dev --name init 

PrismaService – 全局注入

 // src/prisma/prisma.service.ts
import { Injectable。OnModuleInit,OnModuleDestroy } from "@nestjs/common";import { PrismaClient } from "@prisma/client";@Injectable
export class PrismaService extends PrismaClient implements OnModuleInit,OnModuleDestroy {
async onModuleInit { await this.$connect;}
async onModuleDestroy { await this.$disconnect;}
} 

PrismaModule – 全局模块

 // src/prisma/prisma.module.ts
import { Global,Module } from "@nestjs/common";import { PrismaService } from "./prisma.service";@Global
@Module({
providers:,exports:。})
export class PrismaModule {} 

AppModule 引入

 // src/app.module.ts
import { Module } from "@nestjs/common";import { ConfigModule } from "@nestjs/config";import { PrismaModule } from "./prisma/prisma.module";@Module({
imports:,})
export class AppModule {} 

消息写入 – 同步保存文本 & 向量

 // src/messages/messages.service.ts
import { Injectable } from "@nestjs/common";import { PrismaService } from "../prisma/prisma.service";import { EmbeddingService } from "../embedding/embedding.service";// 自封装的 embedding 调用
@Injectable
export class MessagesService {
constructor(
private readonly prisma: PrismaService。private readonly embeddingSrv: EmbeddingService,) {}
/** 插入一条消息并写入对应的向量 */
async createWithVector{
const normalized = content.trim;不过,const vec = await this.embeddingSrv.embed;const vecLit = this.embeddingSrv.toPgVectorLiteral;return this.prisma.$transaction(async tx=>{
const msg = await tx.message.create({
data的观点是。{ conversationId,role,content:normalized },select:{ id:true }
});await tx.$executeRaw`UPDATE messages SET vector=${vecLit}::vector WHERE id=${msg.id}`;
不过,return msg;
}),}
}

会话内语义召回 – 单表完成过滤 + 相似度排序

 // src/messages/messages.service.ts
async searchSimilarInConversation(
conversationId:number。query:string,topK:number=5,excludeMessageId?:number){
const qVec = await this.embeddingSrv.embed;const qLit = this.embeddingSrv.toPgVectorLiteral;const rows:any = await this.prisma.$queryRaw`
SELECT id,role,content。- AS score
FROM messages
WHERE conversation_id=${conversationId}
AND vector IS NOT NULL
${excludeMessageId?this.prisma.sql`AND id <> ${excludeMessageId}` : this.prisma.empty}
ORDER BY vector <=> ${qLit}::vector
LIMIT ${topK}
`;return rows.map}));}

The above query combines:

  • The business filter `WHERE conversation_id=…`,ensuring we only search inside current chat.
  • The similarity ordering using built‑in operator `<=>`。which computes cosine distance when index was created with `VECTOR_COSINE_OPS`.
  • An optional exclusion clause so that current message can be omitted when doing “self‑recall”.

实战演示的观点是,从历史记录中找相似句子

Demos of stored messages :


从user来看,我们团队在选型 Node 后端,NestJS 的 Module 和依赖注入适合拆微服务吗?assistant:NestJS 基于 TypeScript,内置 Module、Controller、Provider 分层…从user来看,另一条线想走 Python:FastAPI 写 AI 接口…assistant:Vue 组合式 API 上手快…说起来,...

If a user later asks “NestJS 和 Python 后端怎么配合”。following call will surface two most relevant prior messages:

typescript // 假设 conversationId 已知为42: const results = await messagesService.searchSimilarInConversation( 42,'NestJS 和 Python 怎么一起工作',10);console.log,/* */

  • 单库方案优势 :强事务保证、一次 SQL 完成过滤+相似度排序、免除双写与 ID 映射维护。适用于多数 Agent 项目在 **百万‑千万** 向量规模下快速落地。说起来,
  • 何时仍需专用向量引擎 :当数据规模突破 **十亿** 級别或 QPS 持续高于 **10k** 时Milvus 等专用 HNSW 实现仍具更佳吞吐与水平 能力。另外如果项目必须同时进行 **混合搜索**且对延迟极致敏感,也可以考虑双库架构。
  • 迁移方法 :从 Postgres+pgvector 起步后如出现规模瓶颈。可逐步将主要向量列迁移至 Milvus,同时保留原有业务表作“源”;迁移过程仅涉及复制向量列,不影响已有业务逻辑。

"


标签: 向量

SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback