(8):实现双删(MySQL+Redis)

目录

  • 🌴一、实现"双路"删除
  • 🌴二、关键代码
    • [1. UserContext 工具类](#1. UserContext 工具类)
    • [2. 删除逻辑:手动编排 RedisSearch 实现"知识同步销毁"](#2. 删除逻辑:手动编排 RedisSearch 实现“知识同步销毁”)
  • 🌴三、踩坑记录
  • [🌴四、 可复用的工程经验](#🌴四、 可复用的工程经验)
    • [1. 向量数据库的"增删改查"同步模版](#1. 向量数据库的“增删改查”同步模版)
    • [2. 可观测性是第一生产力](#2. 可观测性是第一生产力)
    • [3. 框架解耦与底层兜底](#3. 框架解耦与底层兜底)
    • [4. 线程上下文的"清洁工"习惯](#4. 线程上下文的“清洁工”习惯)

✨本篇内容:基于(7)实现数据持久化的情况下,当前端执行删除操作时,MySQL和向量数据库中的内容都能被删除。


🌴一、实现"双路"删除

当我在前端点击"删除"时,文章从MySQL移除,同时系统自动在 Redis 向量库中检索并销毁对应的所有分片。

🌴二、关键代码

1. UserContext 工具类

这是解决 user_id 为空及身份泄露的核心。

java 复制代码
public class UserContext {
    private static final ThreadLocal<Long> USER_ID_HOLDER = new ThreadLocal<>();

    public static void setUserId(Long userId) { USER_ID_HOLDER.set(userId); }
    public static Long getUserId() { return USER_ID_HOLDER.get(); }

    // 在 JwtFilter 的 finally 块中调用,防止线程池污染和内存泄漏
    public static void clear() { USER_ID_HOLDER.remove(); }
}

2. 删除逻辑:手动编排 RedisSearch 实现"知识同步销毁"

由于 LangChain4j 适配器暂不支持 Redis 的批量删除,我们直接操作底层 Jedis 解决了 Not supported yet 报错。

java 复制代码
@Transactional
public void deletePost(Long postId) {
    // 1. MySQL 删除
    postMapper.deleteById(postId);

    // 2. 向量库手动同步清理 (使用原生 RedisSearch 指令)
    try (UnifiedJedis jedis = new UnifiedJedis("redis://127.0.0.1:6379")) {
        // 构建标签查询语法:@{articleId}:{23}
        String queryText = String.format("@articleId:%d", postId);
        
        // 在 article-index 索引中搜索所有关联片段
        SearchResult result = unifiedJedis.ftSearch("article-index", queryText);
        
        if (result.getTotalResults() > 0) {
                System.out.println("--- [Debug] 发现 " + result.getTotalResults() + " 个向量片段 ---");

                for (redis.clients.jedis.search.Document doc : result.getDocuments()) {
                    String redisKey = doc.getId(); // 获取如 article:uuid 这种 Key
                    unifiedJedis.del(redisKey);
                    System.out.println("已物理删除 Redis Key: " + redisKey);
                }
            }
    }
}

🌴三、踩坑记录

坑1:在这里插入代码片`1:Redis 插件缺失

【报错信息】:Factory method 'embeddingStore' threw exception; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR unknown command FT._LIST

【真相】:连接的是普通版 Redis。向量库搜索(RediSearch)是 Redis 的增强模块。

【解决】:使用 Docker 部署

redis/redis-stack 镜像。

坑2:索引配置缺失导致的"数据幽灵"

【报错信息】:[debug]-----result:SearchResult{Total results:0, Documents:[]}(即便数据已在 Redis 中,但检索不到)。

【真相】:Redis 索引中未包含 articleId

字段。RedisSearch 只有在索引中声明的字段才可用于查询。

【解决】:在 AiConfig 中增加

.metadataKeys(Collections.singletonList("articleId")) 并执行 FLUSHALL

重建索引。

技巧:利用docker exec -it redis-stack redis-cli FT.INFO article-index命令来查看属性列表和类型。主义类型是因为在搜索queryText时数字和文本类型要用不同的语法。

坑3:URL 路径变量无法解析

【报错信息】:Required URI template variable 'postId' for method parameter type Long is not present

【真相】:@DeleteMapping("/{id}") 路径中的变量名与参数名 Long postId 不一致。

【解决】:统一变量名,或使用 @PathVariable("id") 显式指定映射。

坑4:路径匹配失败(静态资源混淆)

【报错信息】:org.springframework.web.servlet.resource.NoResourceFoundException: No static resource posts/delete/22

【真相】:Controller 未定义该路径。Spring Boot3 将无法匹配的 URL 默认转向静态资源目录。

【解决】:修正接口路径格式。

坑5:框架接口未实现

【报错信息】:java.lang.UnsupportedOperationException: Not supported yet(在执行embeddingStore.removeAll(filter) 时)。

【真相】:LangChain4j 的 Redis

适配器尚未完整实现批量删除接口。

【解决】:弃用框架 API,引入 UnifiedJedis 直接调用 Redis 原生 FT.SEARCH指令实现手动清理。

坑6:Java 类名冲突

【报错信息】:java: 不兼容的类型: redis.clients.jedis.search.Document 无法转换为 dev.langchain4j.data.document.Document

【真相】:两个框架都定义了 Document类,但语义完全不同。

【解决】:在代码中使用全限定名 redis.clients.jedis.search.Document 进行显式区分。

🌴四、 可复用的工程经验

1. 向量数据库的"增删改查"同步模版

经验:在双存储架构(MySQL + VectorDB)中,删除操作比写入更难。

模式:必须在写入时绑定文章唯一 ID 到元数据(Metadata),删除时采用 "先搜索元数据、再批量销毁 Key" 的原子操作。

2. 可观测性是第一生产力

经验:面对"黑盒"一样的 AI 检索,不要瞎猜。

模式:

后端日志:打印检索得分(Score)、召回片段内容、生成的原始 Prompt。

中间件工具:熟练使用 RedisInsight 查看数据的物理存储格式,利用 FT.INFO 检查索引配置。

3. 框架解耦与底层兜底

经验:框架(LangChain4j)是工具,但不是终点。

模式:当框架能力不足时,应具备直接操控底层原生协议(Jedis/RedisSearch)的能力。这种**"能上能下"**的技术栈掌握能力,是大厂面试官最看重的"硬实力"。

4. 线程上下文的"清洁工"习惯

经验:在处理跨层身份传递(ThreadLocal)时,必须严格遵守生命周期闭环。

模式:JwtFilter 的 finally 块中调用 UserContext.clear(),这是预防高并发环境下身份泄露和内存泄漏的"工业级金标准"。

相关推荐
苏三说技术41 分钟前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎2 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode2 小时前
Redis 在生产项目的使用
前端·后端
用户559822481222 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode2 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战2 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha2 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn2 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425912 小时前
ShardingJDBC
后端
行者全栈架构师2 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端