实训6:基于Redis+PostgreSQL实现对话缓存与持久化
一.介绍
本博客记录在开发智能面试平台过程中,基于Redis实现对话中间状态缓存,并持久化Postgresq
l中,实现断点续答。
在知识库问答场景下,用户可能进行多轮连续对话,每轮对话都需要保存上下文。如果对话记录只存在内存中,用户刷新页面或重新进入就会丢失历史记录,体验很差。
为此,本模块采用冷热分离的存储架构:
Redis热缓存:对话的中间状态实时缓存到Redis,响应速度快
PostgreSQL持久化:对话消息持久化存入数据库,用户下次进入能看到完整的历史记录,实现断点续答
二:设计思路
对话持久化的核心是两张表:rag_chat_sessions(会话表)和rag_chat_messages(消息表)。
会话表负责记录每一次对话的元信息,一个会话可以关联多个知识库。
消息表负责记录每一条具体的消息,消息分两种类型:USER(用户提问)和ASSISTANT(AI回答)。每条消息都有messageOrder字段保证顺序,有completed字段标记流式响应是否完成。
两张表的关系如下:
rag_chat_session->(一对多)rag_chat_message(消息:USER问题+ASSISTANT回答交替存储)
流式响应的处理为:AI回答是流式输出的,不能等全部输出完再存库,所以采用占位符机制:AI开始回答时先创建一条completed=false的空消息占位,等流式输出全部完成后再把完整内容更新进去,同时把completed改为true
二.主要实现
1.会话实体的设计
@Entity
@Table(name = "rag_chat_sessions")
public class RagChatSessionEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 会话标题(自动生成或用户自定义)
private String title;
// 多对多:一个会话可关联多个知识库
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "rag_session_knowledge_bases")
private Set<KnowledgeBaseEntity> knowledgeBases = new HashSet<>();
// 消息数量(冗余字段,避免每次COUNT查询)
private Integer messageCount = 0;
// 是否置顶
private Boolean isPinned = false;
}
2.消息实体设计
@Entity
@Table(name = "rag_chat_messages")
public class RagChatMessageEntity {
// USER 或 ASSISTANT
@Enumerated(EnumType.STRING)
private MessageType type;
// 消息内容
@Column(columnDefinition = "TEXT")
private String content;
// 消息顺序,保证展示顺序正确
private Integer messageOrder;
// 流式响应是否完成
private Boolean completed = true;
}
3.流式响应占位机制
// 流式回答开始前:保存用户消息 + 创建AI消息占位
public Long prepareStreamMessage(Long sessionId, String question) {
// 保存用户消息(completed=true)
RagChatMessageEntity userMessage = new RagChatMessageEntity();
userMessage.setType(MessageType.USER);
userMessage.setContent(question);
userMessage.setCompleted(true);
messageRepository.save(userMessage);
// 创建AI消息占位(completed=false,内容为空)
RagChatMessageEntity assistantMessage = new RagChatMessageEntity();
assistantMessage.setType(MessageType.ASSISTANT);
assistantMessage.setContent("");
assistantMessage.setCompleted(false);
return messageRepository.save(assistantMessage).getId();
}
// 流式回答完成后:更新AI消息内容
public void completeStreamMessage(Long messageId, String content) {
message.setContent(content);
message.setCompleted(true);
messageRepository.save(message);
}
4.Redis缓存封装
// 设置缓存(带过期时间)
public <T> void set(String key, T value, Duration ttl) {
RBucket<T> bucket = redissonClient.getBucket(key);
bucket.set(value, ttl);
}
// 获取缓存,不存在则通过loader加载并缓存
public <T> T getOrLoad(String key, Duration ttl, Function<String, T> loader) {
T value = redissonClient.getBucket(key).get();
if (value == null) {
value = loader.apply(key);
if (value != null) {
redissonClient.getBucket(key).set(value, ttl);
}
}
return value;
}
三.验证结果
1.知识库问答界面
在前端问答界面,选择“机器学习导论”知识库,提问后AI给出了结构化的问答,历史消息也正常展示。

2.会话持久化验证
在DataGrip中查询rag_chat_sessions表,可以看到每次对话都生成了对应的会话记录,包含会话标题、消息数量和创建时间。
SELECT id, title, message_count, created_at FROM rag_chat_sessions LIMIT 5;

3.消息持久化验证
查询rag_chat_messages表,可以看到USER和ASSISTANT的消息交替存储,消息内容、类型、创建时间均正确记录。
SELECT id, type, LEFT(content, 50) as content, created_at FROM rag_chat_messages LIMIT 5;

四.总结
本文实现了知识库问答对话的缓存与持久化功能。
在数据存储层面,设计了rag_chat_sessions和rag_chat_messages两张核心表,会话与消息一对多关联,消息通过messageOrder字段保证顺序,USER和ASSISTANT消息交替存储,结构清晰。
在流式响应处理上,采用占位符机制解决了流式输出的持久化问题。AI开始回答时先创建一条completed=false的空消息,等流式输出全部完成后再一次性更新内容并标记完成。这样既保证了数据的完整性,又避免了每个token都写一次数据库带来的性能问题。
在缓存设计上,通过RedisService对Redisson进行统一封装,提供set、get、getOrLoad等通用操作,所有缓存均设置TTL自动过期,避免内存无限增长。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)