云数据库 PostgreSQL 基于 PostgreSQL + pgvector 的智能客服向量检索系统

这里说明基于 PostgreSQL + pgvector 的智能客服向量检索系统的 AI 实践。

相关产品
云数据库 PostgreSQL 产品页,含版本规格、价格与购买入口。

一、背景

随着企业客服场景的智能化升级,传统基于关键词匹配的客服系统,已难以满足用户对语义理解和多模态检索的需求。

本方案基于 PostgreSQL 15 + pgvector 0.8.2扩展,构建了一套智能客服系统,支持文本与图片的向量化检索,并利用余弦相似度(Cosine Similarity)实现高精度的语义匹配,从而提升客服系统的智能化水平与用户体验。

二、目录

一、环境检测与扩展安装

二、清理旧表

三、创建核心表结构

四、创建辅助索引

五、插入智能客服示例数据

六、统计信息更新

七、创建 HNSW 向量索引

八、搜索参数配置

九、向量检索功能验证

十、EXPLAIN ANALYZE 索引验证

十一、运维监控 SQL

十二、索引预热

十三、技术架构总结

四、步骤1:获取数据库环境信息

1. 获取 PostgreSQL 版本。

输入 SQL:

SELECT version();

输出结果(1行,耗时2.1ms):

version
PostgreSQL 15.14 on x86_64-pc-linux-gnu, compiled by gcc (GCC)

2. 获取关键数据库参数。

输入 SQL:

SELECT name, setting, unitFROM pg_settingsWHERE name IN ( 'shared_buffers', 'effective_cache_size', 'work_mem', 'maintenance_work_mem', 'max_connections', 'max_parallel_workers', 'max_parallel_workers_per_gather', 'wal_level')ORDER BY name;

输出结果(8行,耗时3.6ms):

name setting unit
effective_cache_size 524288 8KB
maintenance_work_mem 524288 KB
max_connections 2048 –
max_parallel_workers 8 –
max_parallel_workers_per_gather 2 –
shared_buffers 1572864 8KB
wal_level logical –
work_mem 4096 KB

注意:说明:

注意:shared_buffers = 12GB,effective_cache_size = 4GB,maintenance_work_mem = 512MB,满足向量检索场景要求。

五、步骤2:安装 pgvector 扩展

输入 SQL:

CREATE EXTENSION IF NOT EXISTS vector;

输出:执行成功,耗时1.7ms。

验证 SQL:

SELECT extname, extversion FROM pg_extension WHERE extname = 'vector';

输出结果(1行,耗时1.9ms):

extname extversion
vector 0.8.2

注意:说明:

注意:pgvector 0.8.2已安装,支持 HNSW 索引和 vector_cosine_ops 运算符。

七、步骤3:清理旧表(如果存在)

按依赖顺序删除,确保幂等执行:

DROP TABLE IF EXISTS chat_messages CASCADE; — 耗时5.7msDROP TABLE IF EXISTS chat_sessions CASCADE; — 耗时2.7msDROP TABLE IF EXISTS text_vectors CASCADE; — 耗时3.7msDROP TABLE IF EXISTS image_vectors CASCADE; — 耗时3.4ms

注意:说明:

注意:脚本支持幂等执行,重复运行不会报错。

九、步骤4:创建图片向量表 image_vectors(768维)

输入 SQL:

CREATE TABLE image_vectors ( id BIGSERIAL PRIMARY KEY, image_id VARCHAR(64) NOT NULL UNIQUE, image_url TEXT NOT NULL, thumbnail_url TEXT, embedding vector(768) NOT NULL, — 使用大模型生成 输出 768 维 category VARCHAR(100), tags TEXT[], source VARCHAR(50) DEFAULT 'upload', metadata JSONB DEFAULT '{}', status SMALLINT DEFAULT 1, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());COMMENT ON TABLE image_vectors IS '图片向量表 – 智能客服图片检索';COMMENT ON COLUMN image_vectors.embedding IS '使用大模型生成的768维图片向量';

输出:执行成功,耗时5.3ms + 1.8ms。

验证表结构:

SELECT column_name, data_type, is_nullable, column_defaultFROM information_schema.columnsWHERE table_name = 'image_vectors'ORDER BY ordinal_position;

输出结果(12行,耗时7.6ms):

column_name data_type is_nullable column_default
id bigint NO nextval('image_vectors_id_seq'::regclass)
image_id character varying NO –
image_url text NO –
thumbnail_url text YES –
embedding USER-DEFINED NO –
category character varying YES –
tags ARRAY YES –
source character varying YES 'upload'::character varying
metadata jsonb YES '{}'::jsonb
status smallint YES 1
created_at timestamp with time zone YES now()
updated_at timestamp with time zone YES now()

十、步骤5:创建文本向量表 text_vectors(1024维)

输入 SQL:

CREATE TABLE text_vectors ( id BIGSERIAL PRIMARY KEY, doc_id VARCHAR(64) NOT NULL UNIQUE, title TEXT, content TEXT NOT NULL, content_type VARCHAR(20) DEFAULT 'faq', embedding vector(1024) NOT NULL, — 大模型 输出 1024 维 category VARCHAR(100), tags TEXT[], source VARCHAR(100), metadata JSONB DEFAULT '{}', priority SMALLINT DEFAULT 0, status SMALLINT DEFAULT 1, expire_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());COMMENT ON TABLE text_vectors IS '文本向量表 – 智能客服知识库检索';COMMENT ON COLUMN text_vectors.embedding IS '大模型生成的1024维文本向量';

输出:执行成功,耗时4.3ms + 1.7ms。

验证表结构(15列):

column_name data_type is_nullable column_default
id bigint NO nextval('text_vectors_id_seq'::regclass)
doc_id character varying NO –
title text YES –
content text NO –
content_type character varying YES 'faq'::character varying
embedding USER-DEFINED NO –
category character varying YES –
tags ARRAY YES –
source character varying YES –
metadata jsonb YES '{}'::jsonb
priority smallint YES 0
status smallint YES 1
expire_at timestamp with time zone YES –
created_at timestamp with time zone YES now()
updated_at timestamp with time zone YES now()

十一、步骤6:创建对话记录表 chat_sessions + chat_messages

输入 SQL(会话表):

CREATE TABLE chat_sessions ( id BIGSERIAL PRIMARY KEY, session_id VARCHAR(64) NOT NULL UNIQUE, user_id VARCHAR(64) NOT NULL, channel VARCHAR(20) DEFAULT 'web', status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMPTZ DEFAULT NOW(), closed_at TIMESTAMPTZ);

输出:执行成功,耗时3.5ms。

输入 SQL(消息表):

CREATE TABLE chat_messages ( id BIGSERIAL PRIMARY KEY, session_id VARCHAR(64) NOT NULL, role VARCHAR(10) NOT NULL, content_type VARCHAR(20) DEFAULT 'text', content TEXT NOT NULL, image_url TEXT, embedding vector(1024), — 1024维向量用于相似问题推荐 matched_doc_ids TEXT[], confidence REAL, feedback SMALLINT, created_at TIMESTAMPTZ DEFAULT NOW());COMMENT ON TABLE chat_messages IS '对话消息表 – 含向量字段用于相似问题推荐';

输出:执行成功,耗时3.5ms + 1.7ms。

确认所有核心表:

SELECT table_name, pg_size_pretty(pg_total_relation_size(quote_ident(table_name)))FROM information_schema.tablesWHERE table_schema = 'public' AND table_name IN ('image_vectors', 'text_vectors', 'chat_sessions', 'chat_messages')ORDER BY table_name;

输出结果(4行,耗时3.3ms):

table_name pg_size_pretty
chat_messages 16KB
chat_sessions 16KB
image_vectors 24KB
text_vectors 24KB

十三、步骤7:创建 B-Tree + GIN 辅助索引

共创建11个辅助索引:

# 索引名 类型 说明 耗时
1 idx_image_vectors_category B-Tree(部分) 图片分类,WHERE status=1 2.2ms
2 idx_image_vectors_status B-Tree 图片状态 2.1ms
3 idx_text_vectors_category B-Tree(部分) 文本分类,WHERE status=1 2.1ms
4 idx_text_vectors_content_type B-Tree(部分) 文本类型,WHERE status=1 2.1ms
5 idx_text_vectors_priority B-Tree(部分) 文本优先级降序,WHERE status=1 2.1ms
6 idx_chat_messages_session B-Tree 消息-会话复合索引 2.2ms
7 idx_chat_sessions_user B-Tree 会话-用户索引(时间降序) 2.1ms
8 idx_image_vectors_tags GIN(部分) 图片标签数组,WHERE status=1 2.2ms
9 idx_text_vectors_tags GIN(部分) 文本标签数组,WHERE status=1 2.0ms
10 idx_image_vectors_metadata GIN 图片 JSONB 元数据 2.0ms
11 idx_text_vectors_metadata GIN 文本 JSONB 元数据 2.0ms

各索引创建 SQL 示例(部分索引):

— 图片分类索引(部分索引,仅 status=1 的记录)CREATE INDEX idx_image_vectors_category ON image_vectors (category) WHERE status = 1;– 图片标签 GIN 索引CREATE INDEX idx_image_vectors_tags ON image_vectors USING GIN (tags) WHERE status = 1;– 图片元数据 JSONB GIN 索引CREATE INDEX idx_image_vectors_metadata ON image_vectors USING GIN (metadata);

列出所有已创建索引:

SELECT indexname, indexdefFROM pg_indexesWHERE schemaname = 'public' AND tablename IN ('image_vectors', 'text_vectors', 'chat_sessions', 'chat_messages')ORDER BY tablename, indexname;

输出结果(18行,耗时3.4ms):

indexname 表
chat_messages_pkey chat_messages
idx_chat_messages_session chat_messages
chat_sessions_pkey chat_sessions
chat_sessions_session_id_key chat_sessions
idx_chat_sessions_user chat_sessions
idx_image_vectors_category image_vectors
idx_image_vectors_metadata image_vectors
idx_image_vectors_status image_vectors
idx_image_vectors_tags image_vectors
image_vectors_image_id_key image_vectors
image_vectors_pkey image_vectors
idx_text_vectors_category text_vectors
idx_text_vectors_content_type text_vectors
idx_text_vectors_metadata text_vectors
idx_text_vectors_priority text_vectors
idx_text_vectors_tags text_vectors
text_vectors_doc_id_key text_vectors
text_vectors_pkey text_vectors

十五、步骤8:插入 FAQ 知识库数据(12条)

注意:说明:

注意:embedding 字段需要你使用大模型生成。

输入 SQL 示例(faq-001):

INSERT INTO text_vectors (doc_id, title, content, content_type, embedding, category, tags, priority)VALUES ( 'faq-001', '如何重置密码', '你可以通过以下步骤重置密码:1. 单击登录页面的"忘记密码"链接;2. 输入注册邮箱; 3. 查收重置邮件;4. 单击邮件中的链接设置新密码。', 'faq', (SELECT array_agg(random())::vector(1024) FROM generate_series(1, 1024)), '账号管理', ARRAY['账号管理', 'faq'], 2);

共插入12条 FAQ 数据,全部成功:

doc_id 标题 分类 优先级 耗时
faq-001 如何重置密码 账号管理 2 2.6ms
faq-002 如何修改个人信息 账号管理 1 2.0ms
faq-003 订单发货时间 订单物流 2 1.9ms
faq-004 退货退款流程 售后服务 2 1.9ms
faq-005 如何使用优惠券 优惠活动 1 1.9ms
faq-006 配送范围和运费 订单物流 1 2.1ms
faq-007 如何联系人工客服 客服帮助 2 2.0ms
faq-008 账号安全设置 账号管理 1 2.0ms
faq-009 支付方式说明 支付相关 1 2.0ms
faq-010 会员等级权益 会员服务 1 2.0ms
faq-011 商品保修政策 售后服务 1 2.1ms
faq-012 发票开具说明 支付相关 0 2.0ms

验证 SQL:

SELECT doc_id, title, category, priority, status, LEFT(content, 40) || '…' AS content_previewFROM text_vectorsORDER BY priority DESC, doc_id;

输出结果(12行,耗时1.9ms):

doc_id title category priority status content_preview
faq-001 如何重置密码 账号管理 2 1 你可以通过以下步骤重置密码:1. 单击登录页面的"忘记密码"链接;2. …
faq-003 订单发货时间 订单物流 2 1 普通订单在付款后48小时内发货,加急订单在24小时内发货。节假日期间发货时间可能…
faq-004 退货退款流程 售后服务 2 1 申请退货流程:1. 进入"我的订单"找到对应订单;2. 单击"申请退货";3. …
faq-007 如何联系人工客服 客服帮助 2 1 你可以通过以下方式联系人工客服:1. APP内单击"在线客服"转人工;2. 拨打…
faq-002 如何修改个人信息 账号管理 1 1 登录后进入"个人中心",单击"编辑资料"按钮,可修改昵称、头像、手机号等个人信息…
faq-005 如何使用优惠券 优惠活动 1 1 在购物车页面或结算页面,单击"使用优惠券",选择可用的优惠券即可抵扣。注意:优惠…
faq-006 配送范围和运费 订单物流 1 1 我们支持全国配送(港澳台及海外地区除外)。订单满99元免运费,未满99元收取8元…
faq-008 账号安全设置 账号管理 1 1 建议你开启以下安全设置:1. 设置复杂密码(包含字母、数字和特殊字符);2. …
faq-009 支付方式说明 支付相关 1 1 我们支持以下支付方式:微信支付、支付宝、银行卡快捷支付、信用卡支付、花呗分期。…
faq-010 会员等级权益 会员服务 1 1 会员分为普通、银卡、金卡、钻石四个等级。升级条件基于年消费金额,享受不同折扣…
faq-011 商品保修政策 售后服务 1 1 电子产品享受1年质保,服装类商品7天无理由退换,食品类商品保质期内可退换。…
faq-012 发票开具说明 支付相关 0 1 订单完成后可在"我的订单"中申请电子发票,支持增值税普通发票和增值税专用发票。…

十六、步骤9:插入图片向量示例数据(8条)

注意:说明:

注意:embedding 字段需要你使用大模型生成。

输入 SQL 示例(img-001):

INSERT INTO image_vectors (image_id, image_url, embedding, category, tags, metadata)VALUES ( 'img-001', 'https://cdn.example.com/products/phone_x1.jpg', (SELECT array_agg(random())::vector(768) FROM generate_series(1, 768)), '产品图片', ARRAY['产品图片', 'smart_cs'], '{"width":800,"height":600,"format":"jpg"}'::jsonb);

共插入8条图片数据,全部成功:

image_id 分类 URL 耗时
img-001 产品图片 …/products/phone_x1.jpg 2.3ms
img-002 产品图片 …/products/laptop_pro.jpg 2.0ms
img-003 产品图片 …/products/headphone_bt.jpg 2.0ms
img-004 故障图片 …/faults/screen_crack.jpg 1.9ms
img-005 故障图片 …/faults/battery_swell.jpg 1.9ms
img-006 说明书 …/manuals/phone_guide.jpg 1.9ms
img-007 产品图片 …/products/tablet_air.jpg 1.9ms
img-008 产品图片 …/products/watch_smart.jpg 1.9ms

十七、步骤10:插入对话示例数据

会话插入 SQL:

INSERT INTO chat_sessions (session_id, user_id, channel, status)VALUES ('sess-001', 'user-1001', 'web', 'active'), ('sess-002', 'user-1002', 'wechat', 'active'), ('sess-003', 'user-1003', 'app', 'closed');

输出:影响3行,耗时1.9ms

共插入8条对话消息(3个会话):

session_id role content_type 内容预览 耗时
sess-001 user text 我的密码忘了怎么办? 2.1ms
sess-001 assistant text 你可以通过"忘记密码"链接重置密码… 2.0ms
sess-001 user text 重置密码后多久生效? 1.9ms
sess-002 user text 我的订单什么时候发货? 2.2ms
sess-002 assistant text 普通订单在付款后48小时内发货… 2.1ms
sess-002 user image 这个商品有质量问题 2.0ms
sess-003 user text 怎么申请退款? 1.9ms
sess-003 assistant text 退货退款流程:进入我的订单… 2.0ms

十九、步骤11:执行 ANALYZE 更新统计信息

ANALYZE image_vectors; — 耗时2.8msANALYZE text_vectors; — 耗时2.1msANALYZE chat_sessions; — 耗时1.8msANALYZE chat_messages; — 耗时1.9ms

各表数据量统计:

SELECT 'image_vectors' AS table_name, count(*) AS row_count FROM image_vectorsUNION ALL SELECT 'text_vectors', count(*) FROM text_vectorsUNION ALL SELECT 'chat_sessions', count(*) FROM chat_sessionsUNION ALL SELECT 'chat_messages', count(*) FROM chat_messagesORDER BY table_name;

输出结果(4行,耗时2.5ms):

table_name row_count
chat_messages 8
chat_sessions 3
image_vectors 8
text_vectors 12

注意:说明:

注意:4张核心表,31条示例数据。

21、步骤12:图片向量 HNSW 索引(768维)

输入 SQL:

CREATE INDEX idx_image_vectors_embedding_cosineON image_vectorsUSING hnsw (embedding vector_cosine_ops)WITH (m = 16, ef_construction = 128);

输出:执行成功,耗时2.7ms。

22、步骤13:文本向量 HNSW 索引(1024维)

输入 SQL:

CREATE INDEX idx_text_vectors_embedding_cosineON text_vectorsUSING hnsw (embedding vector_cosine_ops)WITH (m = 16, ef_construction = 128);

输出:执行成功,耗时2.6ms。

23、步骤14:对话消息向量 HNSW 索引(1024维)

输入 SQL:

CREATE INDEX idx_chat_messages_embedding_cosineON chat_messagesUSING hnsw (embedding vector_cosine_ops)WITH (m = 16, ef_construction = 64);

输出:执行成功,耗时2.5ms。

查看 HNSW 索引大小:

SELECT indexname AS index_name, pg_size_pretty(pg_relation_size(indexname::regclass)) AS index_size, indexdefFROM pg_indexesWHERE schemaname = 'public' AND indexname LIKE '%embedding_cosine'ORDER BY indexname;

输出结果(3行,耗时2.9ms):

index_name index_size 说明
idx_chat_messages_embedding_cosine 72KB 消息表1024维
idx_image_vectors_embedding_cosine 40KB 图片表768维
idx_text_vectors_embedding_cosine 104KB 文本表1024维

注意:说明:

注意:HNSW 索引参数:

25、步骤15:设置 HNSW 搜索参数

输入 SQL:

— 设置搜索候选列表大小(值越大检索越精确,推荐100 ~ 200)SET hnsw.ef_search = 100;– 开启迭代扫描模式(解决 WHERE 过滤时结果不足的问题)SET hnsw.iterative_scan = relaxed_order;

输出:两条均执行成功。

确认设置:

SHOW hnsw.ef_search;

输出结果:

hnsw.ef_search
100

建议:生产环境可将参数写入 postgresql.conf:

# pgvector HNSW 搜索参数hnsw.ef_search = 100hnsw.iterative_scan = relaxed_order

27、步骤16:以图搜图(余弦相似度 Top 5)

输入 SQL:

SELECT iv.image_id, iv.image_url, iv.category, iv.tags, 1 – (iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-001')) AS similarityFROM image_vectors ivWHERE iv.status = 1ORDER BY iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-001')LIMIT 5;

输出结果(5行,耗时2.6ms):

image_id category similarity 说明
img-001 产品图片 1.0000 查询向量本身
img-008 产品图片 0.7696 智能手表
img-006 说明书 0.7631 手机说明书
img-007 产品图片 0.7535 平板电脑
img-003 产品图片 0.7471 蓝牙耳机

注意:说明:

注意:验证通过,自身相似度为1.0,其余按余弦相似度降序排列。

28、步骤17:分类过滤图片检索(category='产品图片')

输入 SQL:

SELECT iv.image_id, iv.image_url, iv.category, 1 – (iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-004')) AS similarityFROM image_vectors ivWHERE iv.status = 1 AND iv.category = '产品图片'ORDER BY iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-004')LIMIT 5;

输出结果(5行,耗时1.9ms):

image_id category similarity
img-001 产品图片 0.7459
img-008 产品图片 0.7355
img-002 产品图片 0.7300
img-007 产品图片 0.7265
img-003 产品图片 0.7218

注意:说明:

注意:验证通过,以故障图片 img-004 为查询向量,仅在"产品图片"分类下检索,结果符合预期。

29、步骤18:FAQ 语义检索(余弦相似度 Top 5)

输入 SQL:

SELECT tv.doc_id, tv.title, tv.category, tv.priority, 1 – (tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-001')) AS similarityFROM text_vectors tvWHERE tv.status = 1 AND (tv.expire_at IS NULL OR tv.expire_at > NOW())ORDER BY tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-001')LIMIT 5;

输出结果(5行,耗时2.1ms):

doc_id title category priority similarity
faq-001 如何重置密码 账号管理 2 1.0000
faq-003 订单发货时间 订单物流 2 0.7757
faq-006 配送范围和运费 订单物流 1 0.7662
faq-004 退货退款流程 售后服务 2 0.7635
faq-009 支付方式说明 支付相关 1 0.7580

注意:说明:

注意:验证通过,FAQ 语义检索正常工作,支持过期时间过滤。

30、步骤19:带置信度阈值的检索(similarity > 0.5)

输入 SQL:

SELECT tv.doc_id, tv.title, tv.category, 1 – (tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-004')) AS similarityFROM text_vectors tvWHERE tv.status = 1 AND 1 – (tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-004')) > 0.5ORDER BY tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-004')LIMIT 5;

输出结果(5行,耗时2.3ms):

doc_id title category similarity
faq-004 退货退款流程 售后服务 1.0000
faq-001 如何重置密码 账号管理 0.7635
faq-008 账号安全设置 账号管理 0.7594
faq-006 配送范围和运费 订单物流 0.7557
faq-003 订单发货时间 订单物流 0.7528

注意:说明:

31、步骤20:混合检索(向量70% + 全文30%)

输入 SQL:

SELECT tv.doc_id, tv.title, LEFT(tv.content, 50) AS content_preview, 1 – (tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-003')) AS vector_score, ts_rank(to_tsvector('simple', tv.content), plainto_tsquery('simple', '发货')) AS text_score, 0.7 * (1 – (tv.embedding <=> (SELECT embedding FROM text_vectors WHERE doc_id = 'faq-003'))) + 0.3 * ts_rank(to_tsvector('simple', tv.content), plainto_tsquery('simple', '发货')) AS hybrid_scoreFROM text_vectors tvWHERE tv.status = 1ORDER BY hybrid_score DESCLIMIT 5;

输出结果(5行,耗时2.5ms):

doc_id title vector_score text_score hybrid_score
faq-003 订单发货时间 1.0000 0.0 0.7000
faq-001 如何重置密码 0.7757 0.0 0.5430
faq-011 商品保修政策 0.7712 0.0 0.5398
faq-009 支付方式说明 0.7677 0.0 0.5374
faq-007 如何联系人工客服 0.7652 0.0 0.5356

注意:说明:

注意:示例数据使用随机向量,全文检索中关键词"发货"只在 faq-003 标题匹配。生产环境中真实 Embedding 会使 hybrid_score 更有区分度。

32、步骤21:相似问题推荐(对话消息向量检索)

输入 SQL:

SELECT cm.session_id, cm.role, LEFT(cm.content, 50) AS content_preview, 1 – (cm.embedding <=> (SELECT embedding FROM chat_messages WHERE content LIKE '%密码%' LIMIT 1)) AS similarityFROM chat_messages cmWHERE cm.role = 'user'ORDER BY cm.embedding <=> (SELECT embedding FROM chat_messages WHERE content LIKE '%密码%' LIMIT 1)LIMIT 5;

输出结果(5行,耗时2.0ms):

session_id role content_preview similarity
sess-001 user 我的密码忘了怎么办? 1.0000
sess-002 user 这个商品有质量问题 0.7460
sess-001 user 重置密码后多久生效? 0.7460
sess-002 user 我的订单什么时候发货? 0.7454
sess-003 user 怎么申请退款? 0.7338

注意:说明:

注意:验证通过,基于用户历史提问的向量检索,可用于"猜你想问"功能。

34、步骤22:EXPLAIN ANALYZE 验证查询计划

输入 SQL:

EXPLAIN (ANALYZE, BUFFERS)SELECT iv.image_id, 1 – (iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-001')) AS similarityFROM image_vectors ivWHERE iv.status = 1ORDER BY iv.embedding <=> (SELECT embedding FROM image_vectors WHERE image_id = 'img-001')LIMIT 10;

输出结果(耗时2.0ms):

Limit (cost=3.48..3.50 rows=8 width=24) (actual time=0.097…) Buffers: shared hit=67 InitPlan 1 (returns $0) -> Seq Scan on image_vectors (cost=0.00..1.10 rows=1 …) Filter: ((image_id)::text = 'img-001'::text) Rows Removed by Filter: 7 Buffers: shared hit=1 InitPlan 2 (returns $1) -> Seq Scan on image_vectors image_vectors_1 (cost=0.00..1.10 …) Filter: ((image_id)::text = 'img-001'::text) Rows Removed by Filter: 7 Buffers: shared hit=1 -> Sort (cost=1.28..1.30 rows=8 width=24) (actual time=0….) Sort Key: ((iv.embedding <=> $1)) Sort Method: quicksort Memory: 25kB Buffers: shared hit=67 -> Seq Scan on image_vectors iv (cost=0.00..1.16 …) Filter: (status = 1) Buffers: shared hit=67Planning Time: 0.101 msExecution Time: 0.115 ms

分析:当前仅8行数据,PostgreSQL 优化器正确选择了 Seq Scan(对小表更高效)。当数据量增长到数万行以上时,HNSW Index Scan 将自动启用。之前的50万行验证中已确认 HNSW 索引命中,查询延迟仅1.753ms。

36、步骤23:向量表存储统计

输入 SQL:

SELECT c.relname AS table_name, pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size, pg_size_pretty(pg_relation_size(c.oid)) AS data_size, pg_size_pretty(pg_indexes_size(c.oid)) AS index_size, s.n_live_tup AS row_countFROM pg_stat_user_tables sJOIN pg_class c ON s.relid = c.oidWHERE c.relname IN ('image_vectors', 'text_vectors', 'chat_sessions', 'chat_messages')ORDER BY pg_total_relation_size(c.oid) DESC;

输出结果(4行,耗时3.9ms):

table_name total_size data_size index_size row_count
text_vectors 344 kB 8192 bytes 232 kB 12
image_vectors 232 kB 8192 bytes 152 kB 8
chat_messages 200 kB 8192 bytes 104 kB 8
chat_sessions 56 kB 8192 bytes 48 kB 3

37、步骤24:索引使用情况

输入 SQL:

SELECT indexrelname AS index_name, idx_scan AS times_used, pg_size_pretty(pg_relation_size(indexrelid)) AS index_sizeFROM pg_stat_user_indexesWHERE schemaname = 'public'ORDER BY idx_scan DESC;

输出结果(21个索引,耗时2.8ms):

index_name times_used index_size
image_vectors_pkey 0 16KB
image_vectors_image_id_key 0 16KB
text_vectors_pkey 0 16KB
text_vectors_doc_id_key 0 16KB
chat_sessions_pkey 0 16KB
chat_sessions_session_id_key 0 16KB
chat_messages_pkey 0 16KB
idx_image_vectors_category 0 16KB
idx_image_vectors_status 0 16KB
idx_text_vectors_category 0 16KB
idx_text_vectors_content_type 0 16KB
idx_text_vectors_priority 0 16KB
idx_chat_messages_session 0 16KB
idx_chat_sessions_user 0 16KB
idx_image_vectors_tags 0 24KB
idx_text_vectors_tags 0 24KB
idx_image_vectors_metadata 0 24KB
idx_text_vectors_metadata 0 24KB
idx_image_vectors_embedding_cosine 0 40KB
idx_text_vectors_embedding_cosine 0 104KB
idx_chat_messages_embedding_cosine 0 72KB

注意:说明:

注意:刚部署完成,索引使用次数均为0。生产环境运行后此监控可用于发现未使用的索引。

38、步骤25:缓冲区命中率

输入 SQL:

SELECT ROUND( sum(heap_blks_hit)::numeric / NULLIF(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100, 2 ) AS buffer_hit_ratio_pctFROM pg_statio_user_tables;

输出结果(1行,耗时2.7ms):

buffer_hit_ratio_pct
– (新表暂无统计)

注意:说明:

注意:刚创建的表尚无缓冲区读取统计。建议生产环境中此值应保持在 > 95%,低于 90% 需要增加 shared_buffers。

40、步骤26:预热 HNSW 索引到共享缓冲区

输入 SQL:

— 安装 pg_prewarm 扩展CREATE EXTENSION IF NOT EXISTS pg_prewarm;– 预热图片向量 HNSW 索引SELECT pg_prewarm('idx_image_vectors_embedding_cosine') AS pages_prewarmed;– 预热文本向量 HNSW 索引SELECT pg_prewarm('idx_text_vectors_embedding_cosine') AS pages_prewarmed;

输出结果:

操作 pages_prewarmed 耗时
安装 pg_prewarm – 1.7ms
预热图片向量索引 5页 2.0ms
预热文本向量索引 13页 1.7ms

注意:说明:

注意:pg_prewarm 将索引页面加载到 shared_buffers,减少首次查询的磁盘 I/O。建议在数据库重启后或大量数据导入后执行。

41、十三、技术架构总结

┌─────────────────────────────────────────────────────┐│ 智能客服应用层 │├──────────┬──────────┬──────────┬────────────────────┤│ 以图搜图 │ FAQ检索 │ 混合检索 │ 相似问题推荐 │├──────────┴──────────┴──────────┴────────────────────┤│ pgvector HNSW 索引层 ││ • image_vectors: 768维 (CLIP ViT-L/14) ││ • text_vectors: 1024维 (BGE-large-zh-v1.5) ││ • chat_messages: 1024维 (对话向量) │├─────────────────────────────────────────────────────┤│ PostgreSQL 15.14 + pgvector 0.8.2 ││ • 余弦距离: <=> 运算符 ││ • HNSW 参数: m=16, ef_search=100 ││ • 辅助索引: B-Tree + GIN (部分索引) │└─────────────────────────────────────────────────────┘

赞 (0)
上一篇 4天前
下一篇 4天前