16_01PostgreSQL 介绍
PostgreSQL 介绍
PostgreSQL 在 AI 项目里的价值,不只是“又一个关系型数据库”,而是它可以把文档、chunk、metadata、embedding 向量、检索条件和任务状态放在一套 SQL 体系里管理。对于第一版 RAG Demo 来说,PostgreSQL + pgvector 是一条比较适合 Java 后端学习 AI Agent 的路线。
[TOC]
本节要了解什么
前面已经开始做 RAG 数据库准备了。
RAG 不是只调用大模型接口。
真正落地时,还要处理一堆数据:
用户上传了什么文档
原文件存在哪里
文档解析后的文本是什么
文本怎么切成 chunkchunk 对应哪个文档、哪一页、哪个标题
chunk 的 embedding 向量是什么
检索时怎么按知识库、用户、文档类型过滤
模型回答时引用了哪些来源
失败任务怎么重试
这些东西都需要数据库承接。
本节先不急着写完整 RAG 代码。
先把 PostgreSQL 讲清楚:
PostgreSQL 是什么
它和 MySQL 有什么区别
为什么 AI/RAG 项目经常选 PostgreSQLpgvector 是什么
PostgreSQL 在 RAG 链路里放在哪里
Java 后端使用 PostgreSQL 要注意什么
第一版学习项目怎么选型
这里先给一个结论:
如果你是 Java 后端,已经熟悉 MySQL,现在要做 AI Agent / RAG,PostgreSQL 值得重点学习,尤其是 PostgreSQL + pgvector + JSONB 这一套。
PostgreSQL 是什么
PostgreSQL,简称 Postgres。
它是一个开源的对象关系型数据库。
可以先简单理解为:
PostgreSQL = 一个功能很强、扩展能力很强、SQL 能力很强的关系型数据库
它和 MySQL 一样,都可以存:
用户表
订单表
配置表
日志表
业务数据
但是 PostgreSQL 的特点是:
SQL 能力更完整
数据类型更丰富
扩展机制更强
JSONB 更好用
全文检索能力更强
适合复杂查询
适合把结构化数据和半结构化数据放在一起处理
可以通过 pgvector 支持向量检索
所以在传统业务系统里,MySQL 很常见。
但在 AI/RAG 项目里,PostgreSQL 经常会被拿出来用。
不是因为 MySQL 不能用,而是因为 PostgreSQL 更适合这一类混合数据场景。
为什么 AI 项目会关心数据库
很多人刚学大模型时,会以为 AI 项目就是:
用户问题
-> 调大模型 API -> 返回答案
这个只是最小聊天 Demo。
一旦进入 RAG、Agent、知识库,就会变成:
上传文档
解析文本
清洗文本
文档切片
生成 embedding存入向量库
用户提问
检索相关 chunk拼接上下文
调用大模型
返回答案和来源
记录调用日志
这里面至少有三类数据。
第一类是结构化数据:
用户
知识库
文档
任务
权限
配置
调用记录
第二类是半结构化数据:
metadata
解析参数
chunk 扩展信息
模型调用参数
检索过滤条件
第三类是向量数据:
问题 embeddingchunk embedding
图片 embedding代码 embedding
PostgreSQL 的优势就在于:
结构化字段:正常建表
半结构化字段:用 JSONB向量字段:用 pgvector过滤和排序:继续用 SQL事务一致性:继续用数据库事务
这对 Java 后端很友好。
因为你不用一开始就拆成一堆组件:
MySQL 存文档
Redis 存状态
专门向量库存 embeddingElasticsearch 做关键词检索
对象存储放原文件
第一版学习项目先用 PostgreSQL 把主链路跑通,成本低很多。
PostgreSQL 在 RAG 里的位置
RAG 的数据库不是只存一个向量。
它要存完整链路。
可以这样看:
graph TD;
A["用户上传文档"] --> B["原始文件<br/>文件系统 / OSS / MinIO"]; A --> C["rag_document<br/>文档元数据"];
C --> D["rag_document_text<br/>raw_text / cleaned_text"]; D --> E["rag_chunk<br/>chunk 文本 / 页码 / 标题路径"];
E --> F["embedding<br/>vector 类型"];
C --> G["rag_ingest_task<br/>解析 / 清洗 / 切片 / 向量化状态"];
E --> H["向量检索<br/>topK chunks"];
H --> I["拼接上下文"];
I --> J["大模型回答"];
J --> K["返回答案和来源"];
PostgreSQL 在这里承担的是:
存文档元数据
存解析文本
存 chunk存 embedding存 metadata存任务状态
支持向量检索
支持来源追踪
所以它不是简单替代 MySQL。
它更像是 RAG 项目的数据底座。
PostgreSQL 和 MySQL 的关系
先说结论:
PostgreSQL 不是用来“吊打 MySQL”的,MySQL 也不是过时了。只是 AI/RAG 这类场景里,PostgreSQL 的组合能力更顺手。
MySQL 依然很适合:
传统业务系统
订单系统
后台管理系统
用户权限系统
高并发读写业务
团队已经非常熟悉的业务库
PostgreSQL 更适合:
复杂 SQL 查询
JSONB metadata 查询
全文检索
向量检索
RAG 知识库
地理信息
报表分析
结构化 + 半结构化混合数据
可以粗略对比一下:
| 方向 | MySQL | PostgreSQL |
|---|---|---|
| 常见场景 | 传统业务系统非常常见 | 复杂数据、分析、AI/RAG 场景更常见 |
| SQL 能力 | 足够大多数业务开发 | 更完整,复杂查询能力更强 |
| JSON 支持 | 支持 JSON | JSONB 生态和索引能力更强 |
| 向量检索 | 新版本和云服务也在发展 | pgvector 生态更成熟,学习资料更多 |
| 全文检索 | 有全文索引能力 | 内置全文检索能力更强,可和向量检索组合 |
| 扩展机制 | 相对弱一些 | 扩展能力强,比如 pgvector、PostGIS |
| Java 使用 | 非常成熟 | 也非常成熟,JDBC/MyBatis/JPA 都支持 |
| 学习成本 | Java 后端通常更熟 | 需要适应一些语法和类型差异 |
如果你的项目已经有 MySQL,不一定要换。
更现实的做法是:
MySQL:继续存原来的业务数据
PostgreSQL:专门承接 RAG 文档、chunk、embedding、检索数据
Redis:按需做缓存、任务进度、限流
第一版学习项目可以更简单:
PostgreSQL + pgvector 直接作为 RAG 主库
这样少一个数据库,少一层同步。
为什么 PostgreSQL 适合 AI/RAG
PostgreSQL 适合 AI/RAG,不是因为它名字高级。
主要是几个点。
能存结构化数据
比如文档表:
CREATE TABLE rag_document (
id uuid PRIMARY KEY, original_name varchar(512) NOT NULL, storage_path varchar(1024) NOT NULL, content_type varchar(128), file_size bigint NOT NULL, status varchar(32) NOT NULL, created_at timestamp NOT NULL DEFAULT now(), updated_at timestamp NOT NULL DEFAULT now());
这种数据用关系型数据库最合适。
因为它有明确字段:
文档 ID文件名
文件大小
文件类型
处理状态
创建时间
更新时间
能存半结构化 metadata
RAG 里经常有很多扩展信息。
比如 chunk 的 metadata:
{
"source": "接口文档.md",
"page": 3, "section": "上传接口",
"parser": "markdown", "language": "zh-CN"}
这些字段不是每个文档都有。
如果全部拆成表字段,会很僵硬。
PostgreSQL 可以用 JSONB:
CREATE TABLE rag_chunk (
id uuid PRIMARY KEY, document_id uuid NOT NULL, chunk_index int NOT NULL, content text NOT NULL, metadata jsonb NOT NULL DEFAULT '{}'::jsonb);
后面可以查:
SELECT *
FROM rag_chunk
WHERE metadata ->> 'section' = '上传接口';
也可以建 GIN 索引:
CREATE INDEX rag_chunk_metadata_gin_idx
ON rag_chunk
USING gin (metadata);
这个对 RAG 很有用。
因为检索时经常不是只看语义相似度,还要带过滤条件:
只查某个知识库
只查某个用户有权限的文档
只查某种文件类型
只查某个业务系统的文档
只查最近上传的资料
能存向量
pgvector 是 PostgreSQL 的向量扩展。
它可以让 PostgreSQL 存 embedding:
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE rag_chunk
ADD COLUMN embedding vector(1536);
这里的 1536 是向量维度。
注意:
向量维度必须和 Embedding 模型输出维度一致
如果模型输出 1024 维,就要用:
embedding vector(1024)
如果维度不一致,插入会失败。
这点很重要。
不要随便复制别人的 vector(1536)。
要先确认你用的 embedding 模型维度。
能做向量相似度搜索
RAG 检索时,一般流程是:
用户问题
-> 生成问题向量
-> 在 chunk embedding 里找语义最相近的几个 chunk -> 把这些 chunk 拼给大模型
SQL 大概这样:
SELECT
id, document_id, content, metadata, embedding <=> :query_embedding AS distanceFROM rag_chunk
WHERE embedding IS NOT NULL
ORDER BY embedding <=> :query_embedding
LIMIT 5;
这里的核心是:
ORDER BY embedding <=> :query_embedding
它表示按向量距离排序。
距离越小,越相似。
能做 metadata + vector 混合查询
真实 RAG 不会只做:
全库向量检索
更常见的是:
在某个知识库里检索
在当前用户有权限的文档里检索
只检索启用状态的文档
只检索中文文档
只检索某个分类下的文档
PostgreSQL 可以这样写:
SELECT
c.id, c.content, c.metadata, c.embedding <=> :query_embedding AS distanceFROM rag_chunk c
JOIN rag_document d ON c.document_id = d.id
WHERE d.status = 'EMBEDDED'
AND d.knowledge_base_id = :knowledge_base_id AND c.embedding IS NOT NULLORDER BY c.embedding <=> :query_embedding
LIMIT 5;
这就是 PostgreSQL 做 RAG 比较舒服的地方。
你既可以做关系型过滤,又可以做向量排序。
pgvector 是什么
pgvector 可以理解为:
让 PostgreSQL 支持向量存储和向量相似度搜索的扩展
它主要解决两个问题:
怎么在 PostgreSQL 里存 embedding怎么在 PostgreSQL 里查最相似的 embedding
启用方式:
CREATE EXTENSION IF NOT EXISTS vector;
创建向量字段:
CREATE TABLE items (
id bigserial PRIMARY KEY, content text NOT NULL, embedding vector(3));
插入测试数据:
INSERT INTO items (content, embedding)
VALUES
('苹果手机', '[0.1, 0.2, 0.3]'),
('安卓手机', '[0.1, 0.2, 0.4]'),
('数据库教程', '[0.8, 0.1, 0.2]');
查询相近数据:
SELECT content, embedding <=> '[0.1, 0.2, 0.35]' AS distance
FROM items
ORDER BY embedding <=> '[0.1, 0.2, 0.35]'
LIMIT 2;
学习阶段可以先用这种小维度理解。
真实 Embedding 模型一般是几百维、1024 维、1536 维或者更多。
向量距离怎么理解
Embedding 本质是一串数字。
比如:
“文件上传限制是多少”
-> [0.12, -0.08, 0.33, ...]
“最大上传文件大小是多少”
-> [0.11, -0.07, 0.35, ...]
这两个问题字面不一样,但语义接近。
所以它们的向量距离通常更近。
pgvector 常见距离方式:
L2 distance:欧氏距离
Inner product:内积
Cosine distance:余弦距离
RAG 文本语义检索里,常见选择是 cosine。
建 HNSW 索引时可以这样:
CREATE INDEX rag_chunk_embedding_hnsw_idx
ON rag_chunk
USING hnsw (embedding vector_cosine_ops);
查询时:
SELECT id, content
FROM rag_chunk
ORDER BY embedding <=> :query_embedding
LIMIT 5;
注意两点:
1. 大批量导入数据时,可以先导入,再建索引
2. 数据量很小时,不建索引也能跑,先把流程跑通更重要
PostgreSQL + pgvector 和专业向量库的区别
专业向量库包括:
Milvus
Qdrant
Weaviate
Pinecone
Chroma
它们更专注向量检索。
PostgreSQL + pgvector 的优势是:
不用多引入一个数据库
SQL 好理解
和业务字段一起查方便
事务一致性好处理
Java 后端接入成本低
适合第一版 RAG Demo
专业向量库的优势是:
大规模向量检索能力更强
向量索引能力更专门
集群能力更强
更适合海量数据和高并发向量查询
学习阶段建议:
第一版:PostgreSQL + pgvector
数据量上来后:再评估 Milvus / Qdrant / Elasticsearch / 云厂商向量服务
不要一开始就上太复杂的架构。
RAG 第一版最重要的是:
能上传
能解析
能切片
能向量化
能检索
能回答
能返回来源
PostgreSQL 和 MySQL 的具体区别
这部分对 Java 后端比较重要。
因为很多 Java 后端以前写 MySQL 多,切到 PostgreSQL 会遇到一些不一样的地方。
自增主键不一样
MySQL 常见:
CREATE TABLE user_info (
id bigint PRIMARY KEY AUTO_INCREMENT, username varchar(64) NOT NULL);
PostgreSQL 可以用:
CREATE TABLE user_info (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username varchar(64) NOT NULL);
也可以用 uuid:
CREATE TABLE rag_document (
id uuid PRIMARY KEY, original_name varchar(512) NOT NULL);
RAG 文档、chunk、任务表,我更建议用 uuid。
原因是:
文档 ID 更适合跨服务传递
不会暴露自增数量
后面拆服务也更方便
布尔类型不一样
MySQL 里很多时候用:
tinyint(1)
PostgreSQL 有真正的 boolean:
is_enabled boolean NOT NULL DEFAULT true
JSON 用法不一样
MySQL 有 JSON。
PostgreSQL 推荐 JSONB。
例如:
metadata jsonb NOT NULL DEFAULT '{}'::jsonb
查询:
SELECT *
FROM rag_chunk
WHERE metadata ->> 'section' = '上传接口';
判断是否包含:
SELECT *
FROM rag_chunk
WHERE metadata @> '{"language":"zh-CN"}'::jsonb;
这个在 RAG metadata 过滤里很常用。
字符串大小写要注意
PostgreSQL 对没加双引号的标识符会转成小写。
建议表名、字段名统一用小写加下划线:
rag_document
rag_document_text
rag_chunk
created_at
updated_at
不要用:
ragDocument
createdAt
否则 SQL 和 ORM 映射会麻烦。
时间类型要提前统一
PostgreSQL 常见时间类型:
timestamp without time zone
timestamp with time zone
学习阶段可以先用:
created_at timestamp NOT NULL DEFAULT now()
如果要做正式项目,建议团队统一时区策略。
比如:
数据库统一 UTC接口返回按用户时区转换
不要一会儿本地时间,一会儿 UTC。
分页语法基本一样
PostgreSQL 和 MySQL 都支持:
SELECT *
FROM rag_document
ORDER BY created_at DESC
LIMIT 20 OFFSET 0;
这个对 Java 后端来说不难。
upsert 写法不一样
MySQL 常见:
INSERT INTO table_name (...) VALUES (...)
ON DUPLICATE KEY UPDATE ...
PostgreSQL 常见:
INSERT INTO rag_document_text (document_id, raw_text, cleaned_text)
VALUES (:document_id, :raw_text, :cleaned_text)
ON CONFLICT (document_id)
DO UPDATE SET
raw_text = EXCLUDED.raw_text, cleaned_text = EXCLUDED.cleaned_text, updated_at = now();
这个后面做“重复解析覆盖文本”时会用到。
PostgreSQL 在 AI 项目里的典型表设计
第一版 RAG 至少可以准备四张表:
rag_document:文档元数据
rag_document_text:解析后的全文
rag_chunk:切片内容和向量
rag_ingest_task:处理任务状态
关系图:
graph TD;
A["rag_document<br/>文档元数据"] --> B["rag_document_text<br/>解析文本"];
A --> C["rag_chunk<br/>切片内容"];
C --> D["embedding<br/>vector"]; A --> E["rag_ingest_task<br/>处理任务"];
C --> F["metadata<br/>页码 / 标题 / 来源"];
D --> G["similarity_search<br/>相似度检索"];
F --> G;
文档表
CREATE TABLE rag_document (
id uuid PRIMARY KEY, knowledge_base_id uuid, original_name varchar(512) NOT NULL, storage_path varchar(1024) NOT NULL, content_type varchar(128), file_size bigint NOT NULL, sha256 varchar(128), status varchar(32) NOT NULL, error_message text, created_at timestamp NOT NULL DEFAULT now(), updated_at timestamp NOT NULL DEFAULT now());
状态可以先定义:
UPLOADED:已上传
PARSED:已解析
CLEANED:已清洗
CHUNKED:已切片
EMBEDDED:已向量化
FAILED:处理失败
文本表
CREATE TABLE rag_document_text (
document_id uuid PRIMARY KEY REFERENCES rag_document(id) ON DELETE CASCADE, raw_text text NOT NULL, cleaned_text text, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamp NOT NULL DEFAULT now(), updated_at timestamp NOT NULL DEFAULT now());
为什么要单独存 raw_text 和 cleaned_text?
因为 RAG 排查离不开中间结果。
如果回答效果差,你要能看:
是不是 PDF 解析丢了内容
是不是清洗时误删了内容
是不是切片前文本就不对
Chunk 表
CREATE TABLE rag_chunk (
id uuid PRIMARY KEY, document_id uuid NOT NULL REFERENCES rag_document(id) ON DELETE CASCADE, chunk_index int NOT NULL, content text NOT NULL, title_path text, page_start int, page_end int, char_start int, char_end int, token_count int, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, embedding vector(1536), created_at timestamp NOT NULL DEFAULT now(), updated_at timestamp NOT NULL DEFAULT now(), UNIQUE (document_id, chunk_index));
如果你暂时还没确定 embedding 模型维度,可以先不加 embedding 字段。
等确认后再加:
ALTER TABLE rag_chunk
ADD COLUMN embedding vector(1024);
任务表
CREATE TABLE rag_ingest_task (
id uuid PRIMARY KEY, document_id uuid NOT NULL REFERENCES rag_document(id) ON DELETE CASCADE, task_type varchar(64) NOT NULL, status varchar(32) NOT NULL, error_message text, started_at timestamp, finished_at timestamp, created_at timestamp NOT NULL DEFAULT now(), updated_at timestamp NOT NULL DEFAULT now());
任务类型:
PARSE
CLEAN
CHUNK
EMBED
任务状态:
PENDING
RUNNING
SUCCESS
FAILED
第一版可以同步处理。
但是表先设计出来,后面改异步任务会更顺。
PostgreSQL 的 JSONB 为什么重要
AI 项目里的很多字段不是固定的。
比如不同解析器可能产生不同 metadata:
PDF:
{
"page": 3, "page_label": "第 3 页",
"parser": "pdfbox"}
Markdown:
{
"heading": "接口说明",
"heading_level": 2, "parser": "markdown"}
DOCX:
{
"paragraph_index": 12, "style": "Heading 2", "parser": "poi"}
这些东西都硬拆字段会很累。
用 JSONB 比较适合:
metadata jsonb NOT NULL DEFAULT '{}'::jsonb
但也不要滥用 JSONB。
原则是:
经常查询、经常过滤、强业务含义的字段,单独建字段
只是补充说明、不同文档类型不一样的内容,放 metadata
比如:
document_id:必须单独字段
chunk_index:必须单独字段
content:必须单独字段
page_start:可以单独字段
parser:可以放 metadatasource_page_label:可以放 metadata
PostgreSQL 的全文检索和混合检索
RAG 不一定只靠向量检索。
向量检索擅长语义相似:
“文件大小限制”
“最大上传容量”
这两个词不一样,但意思接近。
全文检索擅长关键词匹配:
接口名
错误码
订单号
配置项
类名
方法名
比如用户问:
A10001 订单接口在哪个文档里?
这种问题只靠 embedding 未必稳定。
因为 A10001 是精确关键词。
所以后面可以做混合检索:
向量检索:找语义相关 chunk关键词检索:找精确命中的 chunk合并结果
重排序
再交给大模型
PostgreSQL 有内置全文检索能力。
学习阶段可以先不做。
但你要知道后面会用到。
Java 后端怎么连接 PostgreSQL
Java 连接 PostgreSQL 和 MySQL 思路一样。
都是 JDBC。
Spring Boot 里加驱动:
<dependency>
<groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId></dependency>
application.yml 示例:
spring:
datasource: url: jdbc:postgresql://127.0.0.1:5432/ai_rag username: ai password: ai_password driver-class-name: org.postgresql.Driver
如果使用 MyBatis,也差不多:
mybatis:
mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true
字段建议继续用下划线:
created_at
updated_at
document_id
chunk_index
Java 里映射成:
createdAt
updatedAt
documentId
chunkIndex
Docker 启动 PostgreSQL + pgvector
学习阶段建议直接 Docker Compose。
创建 docker-compose.yml:
services:
postgres: image: pgvector/pgvector:pg16 container_name: ai-rag-postgres environment: POSTGRES_DB: ai_rag POSTGRES_USER: ai POSTGRES_PASSWORD: ai_password ports: - "5432:5432" volumes: - ai_rag_pg_data:/var/lib/postgresql/data
volumes:
ai_rag_pg_data:
启动:
docker compose up -d```
查看容器:
```bash
docker ps```
连接数据库:
```bash
psql postgresql://ai:ai_password@127.0.0.1:5432/ai_rag```
如果本机没有 psql,也可以进容器:
```bash
docker exec -it ai-rag-postgres psql -U ai -d ai_rag```
启用 vector 扩展:
```sql
CREATE EXTENSION IF NOT EXISTS vector;
检查扩展:
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'vector';
推荐初始化 SQL 文件
建议放到:
db/init/001_rag_schema.sql
内容顺序:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE rag_document (...);
CREATE TABLE rag_document_text (...);
CREATE TABLE rag_chunk (...);
CREATE TABLE rag_ingest_task (...);
CREATE INDEX ...;
后面可以用 Flyway 管理:
V1__init_rag_schema.sql
V2__add_embedding_column.sql
V3__add_search_log.sql
学习阶段先手动执行 SQL 也可以。
但不要把建表 SQL 散落在聊天记录里。
要放进项目目录。
PostgreSQL 在 Spring AI 里的位置
如果你后面用 Spring AI,它有 VectorStore 抽象。
可以理解为:
Java 代码不直接关心底层是 pgvector、Milvus 还是 Redis Vector统一通过 VectorStore 写入和检索 Document
大概逻辑是:
解析文档
-> 生成 Document 对象
-> EmbeddingModel 生成向量
-> VectorStore 保存
-> Retriever 检索
-> ChatClient 生成回答
PostgreSQL + pgvector 可以作为 Spring AI 的一个向量存储实现。
对你现在的学习路线来说,可以先按两层理解:
底层:PostgreSQL + pgvector 能存向量、查向量
上层:Spring AI 封装 VectorStore,帮你接入 RAG 流程
先理解底层,再用框架。
这样不容易被框架绕晕。
第一版 RAG 项目怎么用 PostgreSQL
第一版不要做太大。
建议目标:
上传一个 Markdown / TXT 文档
解析成文本
切成 chunk调用 embedding 模型生成向量
存 PostgreSQL用户提问
检索 topK chunks拼接 Prompt调用大模型回答
返回答案和来源
第一版数据链路:
graph LR;
A["上传文档"] --> B["rag_document"];
B --> C["rag_document_text"]; C --> D["rag_chunk"]; D --> E["embedding vector"]; E --> F["topK 检索"];
F --> G["Prompt 上下文"];
G --> H["LLM 回答"];
H --> I["答案 + 来源"];
接口可以先设计成:
POST /api/rag/documents/upload
POST /api/rag/documents/{id}/parse
POST /api/rag/documents/{id}/chunk
POST /api/rag/documents/{id}/embed
POST /api/rag/chat
GET /api/rag/documents/{id}
GET /api/rag/documents/{id}/chunks
不要一上来就做:
多租户
复杂权限
图片 OCR表格结构化解析
知识图谱
自动评测
分布式向量索引
这些都可以后面加。
第一版先让主链路通。
MySQL 还要不要学
要。
MySQL 对 Java 后端还是基本功。
但是现在你要转 AI Agent / RAG,PostgreSQL 也必须补。
可以这样分工学习:
MySQL:继续作为业务系统数据库基础
PostgreSQL:重点学习 RAG、pgvector、JSONB、复杂查询
Redis:缓存、会话、任务进度、限流
Elasticsearch:关键词检索、日志检索、混合检索扩展
不要陷入“只能选一个数据库”的误区。
工程里经常是组合使用。
只是学习第一版时,不要组件太多。
常见问题
PostgreSQL 会不会比 MySQL 难很多
会有一点不适应。
主要是:
类型更丰富
JSONB 语法不一样
自增主键写法不一样
schema 概念更明显
SQL 更严格一点
但你有 Java 后端经验,这个学习成本不高。
真正要花时间的是:
pgvector
metadata 过滤
RAG 表设计
向量索引
检索质量排查
PostgreSQL 能不能替代 MySQL
能,但不一定需要。
如果是新项目,可以直接 PostgreSQL。
如果老项目已经是 MySQL,不建议为了 RAG 把整个业务库迁移到 PostgreSQL。
更稳的方式是:
老业务继续 MySQLRAG 模块单独 PostgreSQL服务层做整合
第一版需要 Redis 吗
不一定。
第一版同步处理也可以。
Redis 后面适合放:
任务进度
接口限流
会话缓存
热点问答缓存
分布式锁
如果现在加 Redis 会拖慢进度,可以先不加。
第一版需要专业向量库吗
不需要。
先用 PostgreSQL + pgvector。
等你遇到明确瓶颈,再考虑专业向量库。
明确瓶颈包括:
向量数量很大
检索延迟明显不可接受
高并发检索压力大
需要更复杂的向量索引能力
学习阶段不要过度架构。
embedding 维度怎么确定
看你用的 Embedding 模型文档。
数据库字段必须和模型输出维度一致。
比如:
模型输出 768 维:vector(768)
模型输出 1024 维:vector(1024)
模型输出 1536 维:vector(1536)
不要猜。
也不要模型换了,数据库字段不改。
metadata 都放 JSONB 里可以吗
不建议。
原则还是:
高频过滤字段单独建列
低频扩展字段放 JSONB
比如:
knowledge_base_id:单独字段
user_id:单独字段
document_id:单独字段
status:单独字段
page_label:可以放 JSONBparser_version:可以放 JSONB
这样查询和索引更清楚。
练习清单
完成几件事情:
能说清 PostgreSQL 是什么
能说清 PostgreSQL 和 MySQL 的主要区别
能解释为什么 RAG 项目适合 PostgreSQL + pgvector能用 Docker 启动 PostgreSQL + pgvector能进入 psql 执行 SQL能启用 vector 扩展
能创建 rag_document 表
能创建 rag_document_text 表
能创建 rag_chunk 表
能创建 rag_ingest_task 表
能解释 embedding vector 维度为什么不能乱写
能写一个 topK 向量检索 SQL能说明 JSONB metadata 的使用边界
能说明 MySQL 在 AI 项目里还能放什么
建议目录:
ai-agent-study
├── db
│ └── init
│ └── 001_rag_schema.sql
├── docs
│ ├── postgresql-ai-intro.md
│ └── database-prepare.md
├── docker-compose.yml
└── ai_study
├── ai_demo └── python_study
小结
本节的结论:
PostgreSQL 适合 AI/RAG,不是因为它比 MySQL“高级”,而是因为它能把关系数据、JSONB metadata、全文检索和 pgvector 向量检索放在同一套 SQL 体系里。
你可以这样记:
MySQL:传统业务系统基本功
PostgreSQL:AI/RAG 数据底座重点补充
pgvector:让 PostgreSQL 能存向量、查向量
JSONB:让 metadata 更灵活
SQL 过滤 + 向量排序:RAG 检索的核心组合
第一版 RAG 不要复杂化。
先跑通:
文档
-> 文本
-> chunk -> embedding -> PostgreSQL -> topK 检索
-> 大模型回答
-> 来源返回
后面再考虑 Redis、Elasticsearch、Milvus、Qdrant、权限、多租户和评测。