首页 / Spring AI 入门教程 / 持久化记忆

Spring AI 入门教程

持久化记忆

本教程共 45 篇 · 第 23 篇 · 更新于 2026-08-16 · 约 8 分钟阅读

Spring AI持久化记忆JdbcChatMemoryRepositoryCassandraNeo4jChatMemoryRepositoryTTL选型

本节目标:把聊天记忆存进数据库。学完你能对比内存与各数据库实现,会配置 JDBC 持久化,能按场景选型。

23.1 为什么要持久化

内存记忆有个硬伤:进程重启就丢。开发时无所谓,生产环境不行。用户昨天聊的内容,服务发版重启,全没了。多实例部署更麻烦,每个实例各存一份,同一个用户在不同实例上记忆对不上。

数据库存储解决这些问题。消息落到磁盘,重启不丢;多个实例连同一个库,记忆天然共享;还能做审计、按时间清理。Spring AI 把持久化做成了统一的 Repository 抽象,换存储只需换依赖和实现。

23.2 统一抽象 ChatMemoryRepository

上一章说过,ChatMemoryRepository 只负责存和取消息,ChatMemory 决定留哪些。持久化实现都是这个接口的落地。官方内置的实现有这些:

实现存储
InMemoryChatMemoryRepositoryJVM 内存
JdbcChatMemoryRepository关系型数据库
CassandraChatMemoryRepositoryApache Cassandra
Neo4jChatMemoryRepositoryNeo4j 图数据库
MongoChatMemoryRepositoryMongoDB
RedisChatMemoryRepositoryRedis Stack

用法完全一致:注入 Repository,塞给 MessageWindowChatMemory。所有实现取消息都按从旧到新的顺序返回,正好是模型要的对话顺序。项目里配了哪个 Repository,自动配置的 ChatMemory 就用哪个。

除了官方内置,Azure Cosmos DB 也有社区维护的外部实现,文档在 azurecosmosdb.github.io。用的还是同一套接口,接入方式和内置实现没有区别。想接别的存储,实现 ChatMemoryRepository 接口就行,官方留了自定义的口子。

23.3 JDBC:最常见的持久化方案

关系型数据库普及度最高,JDBC 实现是大多数项目的首选。先加依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>

Spring AI 为它提供自动配置,直接在代码里注入:

@Autowired
JdbcChatMemoryRepository chatMemoryRepository;

@Bean
public ChatMemory chatMemory(JdbcChatMemoryRepository repository) {
    return MessageWindowChatMemory.builder()
            .chatMemoryRepository(repository)
            .maxMessages(10)
            .build();
}

启动时会自动创建 SPRING_AI_CHAT_MEMORY 表。默认只在嵌入式数据库(H2 等)初始化,外接 MySQL 要显式打开:

spring.datasource.url=jdbc:mysql://localhost:3306/spring_ai_yt
spring.datasource.username=root
spring.datasource.password=root1234
spring.ai.chat.memory.repository.jdbc.initialize-schema=always

initialize-schema 有三个值:embedded 只在嵌入式库初始化(默认)、always 每次都初始化、never 完全交给 Flyway 这类工具管理。想改脚本位置,用 spring.ai.chat.memory.repository.jdbc.schema 指定 classpath 路径。

方言抽象让一套代码适配多种数据库。官方开箱支持 PostgreSQL、MySQL/MariaDB、SQL Server、HSQLDB、Oracle。根据 JDBC URL 自动识别方言,也能手动指定:

ChatMemoryRepository repository = JdbcChatMemoryRepository.builder()
        .jdbcTemplate(jdbcTemplate)
        .dialect(new PostgresChatMemoryRepositoryDialect())
        .build();

想支持别的数据库,实现 JdbcChatMemoryRepositoryDialect 接口,提供增删查的 SQL 就行。

这里提醒 1.x 升级的用户:2.0 的表结构加了 sequence_id 列,专门管消息排序,旧库要按升级指南加列,否则启动或查询会出问题。

消息按 sequence_id 列排序,保证对话顺序。每条消息还有创建时间戳,存在元数据的 CONVERSATION_TS 键里,UI 要显示消息时间可以直接读:

List<Message> messages = chatMemory.get(conversationId);
for (Message message : messages) {
    Instant createdAt = (Instant) message.getMetadata()
            .get(JdbcChatMemoryRepository.CONVERSATION_TS);
}
Note

JdbcChatMemoryRepository 不保存工具调用消息。含工具调用的 AssistantMessage 和 ToolResponseMessage 在保存时会被静默过滤。要用工具又要完整持久化,官方推荐 Spring AI Session 项目,它能正确保存所有消息类型。

23.4 Cassandra:时间序列与 TTL

Cassandra 是分布式列式数据库,主打高可用、可扩展。它的记忆实现采用时间序列结构,所有历史会话窗口都留存,适合合规审计。消息按时间戳升序返回。

官方建议给消息设置 TTL,比如三年。到期自动清理,不用写定时任务:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-chat-memory-repository-cassandra</artifactId>
</dependency>

常用配置:

spring.cassandra.contact-points=127.0.0.1
spring.cassandra.port=9042
spring.ai.chat.memory.cassandra.keyspace=springframework
spring.ai.chat.memory.cassandra.table=ai_chat_memory
spring.ai.chat.memory.cassandra.time-to-live=94608000

time-to-live 单位是秒,94608000 秒正好三年。表由自动配置创建,不想自动建表,把 spring.ai.chat.memory.cassandra.initialize-schema 设为 false。消息列名也能改,用 messages-column 属性指定。

不用自动配置的话,手动创建也简单,提供一个 CQL 会话就行:

ChatMemoryRepository repository = CassandraChatMemoryRepository
        .create(CassandraChatMemoryRepositoryConfig.builder()
                .withCqlSession(cqlSession));

23.5 Neo4j:图数据库方案

Neo4j 把消息存成图中的节点和关系。会话是 Session 节点,消息是 Message 节点,工具调用、元数据、媒体都有对应的节点标签。适合想利用图结构做关联分析的场景。

依赖和用法与前面一致:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId>
</dependency>
@Autowired
Neo4jChatMemoryRepository chatMemoryRepository;

@Bean
public ChatMemory chatMemory(Neo4jChatMemoryRepository repository) {
    return MessageWindowChatMemory.builder()
            .chatMemoryRepository(repository)
            .maxMessages(10)
            .build();
}

节点标签可以用配置改,默认是 Session、Message、ToolCall、ToolResponse、Metadata、Media。启动时自动建索引,不需要手动初始化表结构。它是这些持久化实现里少数完整支持工具调用消息的。

改标签的配置示例:

spring.ai.chat.memory.repository.neo4j.session-label=Conversation
spring.ai.chat.memory.repository.neo4j.message-label=ChatMessage

改了标签,建索引时也会自动按新标签处理。

23.6 其他实现:Mongo 与 Redis

Mongo 适合文档型数据,实现按时间戳排序,支持 TTL。TTL 默认关闭,想自动过期就配置:

spring.ai.chat.memory.repository.mongo.ttl=86400
spring.ai.chat.memory.repository.mongo.create-indices=true

ttl 单位是秒,86400 就是一天。create-indices 控制启动时是否自动建索引。

Redis 实现基于 Redis Stack 7.0+,用 RedisJSON 存消息,自动建搜索索引。低延迟、支持 TTL,还能做高级查询:按消息类型、内容、时间范围、元数据检索。适合延迟敏感的在线场景。

常用配置:

spring.ai.chat.memory.redis.index-name=chat-memory-idx
spring.ai.chat.memory.redis.key-prefix=chat-memory:
spring.ai.chat.memory.redis.time-to-live=24h

time-to-live 支持 24h、30d 这类写法,不设就永不过期。消息按 JSON 文档存储,检索靠搜索索引,上限是每会话 1000 条。

不想用自动配置,手动建也直接:

RedisClient jedisClient = RedisClient.builder()
        .hostAndPort("localhost", 6379)
        .build();

ChatMemoryRepository repository = RedisChatMemoryRepository.builder()
        .jedisClient(jedisClient)
        .indexName("my-chat-index")
        .keyPrefix("my-chat:")
        .timeToLive(Duration.ofHours(24))
        .build();

23.7 怎么选

方案特点适合
内存零配置,重启丢失开发调试、原型
JDBC生态成熟,SQL 好查大多数业务系统
Cassandra高可用,TTL 自动清理海量数据、审计留痕
Neo4j图结构,支持工具消息关系分析、复杂查询
Mongo文档灵活,TTL已在用 Mongo 的团队
Redis低延迟,高级查询在线高并发场景

选型就两条主线。已有数据库基础设施的,优先 JDBC,省心。数据量大或要自动过期的,看 Cassandra;延迟敏感的看 Redis。个人项目和生产小系统,先内存起步,需要时平滑换 JDBC。业务代码只依赖 ChatMemory 抽象,换实现基本不动代码。

落到具体决策,问自己三个问题。会话需要跨重启保留吗?不需要就停在内存。团队已有的数据库是什么?有 MySQL 就 JDBC,有 Mongo 就 Mongo,别为记忆单独引一套新系统。数据量和延迟要求高吗?单机扛得住就保持简单,扛不住再看 Cassandra 或 Redis。多数项目的答案,是 JDBC 加 MySQL,够用且好维护。

23.8 小结

持久化解决重启丢失和多实例共享。Repository 抽象让换存储只改依赖和配置。JDBC 最通用,Cassandra 强在 TTL 和海量数据,Neo4j 支持工具消息和图分析,Mongo、Redis 各有适用场景。选型跟着现有设施和数据量走,别过度设计。