😲 前言:Redis 还能干这个?
提到 Redis,你的第一反应是什么?
- 分布式缓存?
- 分布式锁?
- 简单的消息队列?
如果我告诉你,现在的 Redis 可以存储图片特征,并且能在毫秒级内从百万张图片中找到最相似的那一张,你信吗?
在 AI 大爆发的今天,向量数据库 (Vector DB)成了新宠(如 Milvus, Pinecone)。但对于大多数中小项目来说,为了一个搜图功能去部署一套全新的复杂的向量数据库,运维成本太高了。
其实,你手边的 Redis 早就支持了!
利用Redis Stack (RediSearch),我们不需要引入任何新组件,就能在 Java 项目中实现炫酷的“以图搜图”。
今天,我们就用Spring Boot + Redis + 深度学习模型,从零打造这个功能!
🧠 核心原理:图片是怎么“变成”索引的?
计算机看不懂图片,它只看得懂数字。
“以图搜图”的核心流程分为两步:
- 特征提取 (Embedding):利用 AI 模型(如 ResNet, VGG, CLIP),把一张图片转换成一个512 维或 1024 维的浮点数向量 (Vector)。
- 向量检索 (Vector Search):在数据库中计算这个向量与其他所有图片向量的距离(欧氏距离或余弦相似度)。距离越近,图片越相似。
Redis 的角色:它不再存储简单的 String,而是存储 Vector,并利用 HNSW 算法构建索引,加速比对过程。
架构图解:
🛠️ 环境准备:给 Redis 装上“大脑”
普通的 Redis 是不支持向量搜索的,我们需要Redis Stack。
最快的方法是用 Docker 启动:
docker run -d --name redis-stack -p6379:6379 -p8001:8001 redis/redis-stack:latest启动后,访问http://localhost:8001可以看到可视化后台,非常爽。
💻 Java 实战:代码撸起来
1. 引入依赖
我们需要jedis(4.x 版本以上支持更好) 和一个用于处理图片的 AI 库(这里为了简化,假设我们已经有一个工具类能把图片转向量,实际可以使用Deep Java Library (DJL)或调用 Python API)。
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.0.0</version></dependency>2. 建立向量索引 (Schema)
在 Redis 中,我们需要先定义一个索引,告诉它:“我要存向量了,用 HNSW 算法,维度是 512”。
importredis.clients.jedis.JedisPooled;importredis.clients.jedis.search.IndexDefinition;importredis.clients.jedis.search.IndexOptions;importredis.clients.jedis.search.Schema;publicvoidcreateIndex(JedisPooledjedis){// 定义 SchemaSchemaschema=newSchema().addTextField("name",1.0)// 图片名称.addVectorField("image_vector",Schema.VectorField.VectorAlgo.HNSW,// 核心配置:512维,浮点数,欧氏距离(L2)newHashMap<String,Object>(){{put("TYPE","FLOAT32");put("DIM",512);put("DISTANCE_METRIC","L2");}});// 创建索引 idx:imagesIndexDefinitiondef=newIndexDefinition().setPrefixes("img:");try{jedis.ftCreate("idx:images",IndexOptions.defaultOptions().setDefinition(def),schema);System.out.println("✅ 向量索引创建成功!");}catch(Exceptione){System.out.println("⚠️ 索引已存在,跳过。");}}3. 图片入库 (Vector Storage)
当用户上传图片时,我们将其转化为向量,存入 Redis Hash 结构中。
publicvoidaddImage(Stringid,Stringname,float[]vector){Map<String,Object>fields=newHashMap<>();fields.put("name",name);// ⚠️ 重点:Redis 需要二进制格式的向量fields.put("image_vector",floatArrayToByteArray(vector));// 存入 Hash,Key 以 img: 开头jedis.hset("img:"+id,fields);}// 辅助方法:float[] 转 byte[] (小端序)// 实际开发中可以使用 ByteBuffer 转换4. 以图搜图 (KNN Search)
最激动人心的一步来了!拿着一张新图片的向量,去 Redis 里搜 Top 5。
importredis.clients.jedis.search.Query;importredis.clients.jedis.search.SearchResult;publicvoidsearchSimilarImages(float[]targetVector){// 构造查询语句:查找最近的 5 个邻居 (KNN 5)// 语法:*=>[KNN 5 @image_vector $BLOB AS score]StringqueryStr="*=>[KNN 5 @image_vector $BLOB AS score]";Queryquery=newQuery(queryStr).addParam("BLOB",floatArrayToByteArray(targetVector))// 传入待搜图片的向量.returnFields("name","score")// 返回图片名和相似度分数.setSortBy("score",true)// 按相似度排序.dialect(2);// 必须开启方言 2SearchResultresult=jedis.ftSearch("idx:images",query);System.out.println("🔍 找到 "+result.getTotalResults()+" 张相似图片:");result.getDocuments().forEach(doc->{Stringname=doc.getString("name");Doublescore=(Double)doc.get("score");System.out.println("图片: "+name+", 距离: "+score);});}🚀 效果演示
假设库里有“猫”、“狗”、“汽车”的图片。
- 你上传了一张**“波斯猫”**的图片。
- 程序将其转为向量,扔给 Redis。
- Redis 瞬间返回了库里所有的**“猫”,且那张“布偶猫”**排在第一位(距离最近)。
- 整个过程耗时不到50ms!
这就是向量搜索的魅力。它不再匹配关键词“猫”,而是理解了图片的**“视觉特征”**。
📝 总结
为什么要在 Redis 做这件事?
- 架构简单:不用维护 Milvus、Elasticsearch,一个 Redis 全搞定。
- 性能炸裂:基于内存的 HNSW 索引,速度极快。
- 生态成熟:Redis 大家都懂,学习成本极低。
旧瓶装新酒,Redis 早就不是当年的那个 KV 缓存了。赶紧把这个功能加到你的毕业设计或公司项目里,绝对是让面试官眼前一亮的杀手锏!
博主留言:
想要获取完整的 Java 工程代码(包含 DeepLearning4j 图片转向量的实现)?
点赞 + 收藏,评论区回复“搜图”,源码直接发你!一起玩转 AI 黑科技!