专业网站设计建设公司,抖音代运营公司排名前十强,做app的公司有哪些,网站上线要准备什么整理不易#xff0c;请不要吝啬你的赞和收藏。 1. 前言 写这篇文章挺不容易的#xff0c;网络上对于 SpringBoot 实现 Redis 向量相似性搜索的文章总体来说篇幅较少#xff0c;并且这些文章很多都写得很粗糙#xff0c;或者不是我想要的实现方式#xff0c;所以我不得不阅… 整理不易请不要吝啬你的赞和收藏。 1. 前言
写这篇文章挺不容易的网络上对于 SpringBoot 实现 Redis 向量相似性搜索的文章总体来说篇幅较少并且这些文章很多都写得很粗糙或者不是我想要的实现方式所以我不得不阅读大量官方文档边试错边修改这花费了不少时间不过好在结果还不错。 话不多说下面说下这篇文章。这篇文章将介绍两种实现方式 第一种为使用 Jedis 中的 UnifiedJedis 类实现 第二种为使用 SpringAI 中的 VectorStore 实现。通过这边文章你将收获如何使用阿里百炼 Embedding 模型实现文本向量化如何通过连接池获取 UnifiedJedis 对象如何在 SpringBoot 中实现向量数据的存储以及使用 fTSearch 进行向量相似性搜索如何使用 SpringAI 的 VecotStore。 2. 前提条件 已安装 Redis Stack 如何安装请参考 docker compose 安装 Redis Stack 。已对 Redis 作为向量库有了解如不了解请参考 Redis 作为向量库入门指南。项目中引入了 Spring AI Alibaba 如何引入请参考 Spring AI Alibaba 的简单使用 。 项目中引入了 Redis 如何引入请参考 SpringBoot 引入 redis 。 3. 使用 UnifiedJedis 实现 注这个方式需要你先创建向量索引如果不了解请参考 Redis 作为向量库入门指南 。需要注意的是这个方式基于创建的索引数据类型为 Hash 。 3.1 引入依赖
我的 SpringBoot 版本为3.2.4spring-ai-alibaba-starter 版本升级为了 1.0.0-M3.2jdk 版本为17。 注需要注意我之前文章使用的 Redis 客户端为 lettuce但因其对 RedisSearch 语法支持较差所以我改为使用 Jedis。Jedis 中的 UnifiedJedis 类对 RedisSeach 语法有很好的支持。 3.1.1 引入 Redis 客户端
我们使用 spring-boot-starter-data-redis 来集成和管理 Redis 使用 Jedis 作为 Java 中 Redis 的客户端注意 SpringBoot2.0 默认使用 lettuce 作为客户端需要排除 lettuce-core 包。 我的 spring-boot-starter-data-redis 版本为 3.2.4jedis 的版本为 5.2.0。 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdexclusionsexclusiongroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactId/exclusion/exclusions
/dependencydependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId
/dependency 注建议引入 spring-boot-starter-parent 用来管理 SpringBoot 项目的依赖版本和构建配置。 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.2.4/version
/parent 3.1.2 引入 Spring AI Alibaba
这个实现方式中项目中引入 spring-ai-alibaba 主要是为了调用阿里百炼 Embedding 模型实现文本向量化如果你有其它的文本向量化包可以改为引用其它的。 我使用的版本是 1.0.0-M3.2对应 Spring AI 的版本为 1.0.0-M3。 dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter/artifactId
/dependency 3.2 添加配置
3.2.1 添加 redis 配置
application.yml 文件中添加redis相关配置包含 host、port、password、连接池信息等。 spring:data:redis:database: 0host: 127.0.0.1port: 6379password: gusy1234jedis:pool:# 连接池最大连接数max-active: 8# 连接池最大空闲连接数max-idle: 8# 连接池最大阻塞等待时间负值表示没有限制max-wait: 0# 连接池最小空闲连接数min-idle: 2# 连接超时时间毫秒timeout: 1000 3.3 配置连接池
3.3.1 使用自定连接池
这个是我读 Jedis 代码摸索出来的也用了一些时间琢磨在此做个记录吧更推荐使用 第二种。使用自定义连接池管理 UnifiedJedis Bean
public UnifiedJedis unifiedJedis() {HostAndPort hostAndPort new HostAndPort(host, port);// 设置 Jedis 客户端配置DefaultJedisClientConfig jedisClientConfig DefaultJedisClientConfig.builder().password(password).database(database).build();// 设置连接池参数ConnectionPoolConfig poolConfig new ConnectionPoolConfig();poolConfig.setMaxTotal(maxActive);poolConfig.setMaxIdle(maxIdle);poolConfig.setMinIdle(minIdle);PooledConnectionProvider provider new PooledConnectionProvider(hostAndPort, jedisClientConfig, poolConfig);return new UnifiedJedis(provider);
} 3.3.2 推荐使用 JedisPooled
比较意外的是在尝试使用 Spring AI 的 Redis VectorStore 实现时阅读源码发现了一个更好的创建 UnifiedJedis 连接池的方式并且是 Redis 推荐的方式 Redis 官方文档就是使用 JedidsPooled。 JedisPooled 继承自 UnifiedJedis。JedisPooled 在 Jedis 版本 4.0.0 中添加提供了 类似于 JedisPool 的功能但具有更直接的 API。相比于 JedisPool JedisPooled 作为连接池更为简单其不需要为每个命令添加一个 try-with-resources 块。 Bean
public JedisPooled jedisPooled() throws URISyntaxException {ConnectionPoolConfig poolConfig new ConnectionPoolConfig();// 池中最大活跃连接数默认 8poolConfig.setMaxTotal(maxActive);// 池中最大空闲连接数默认 8poolConfig.setMaxIdle(maxIdle);// 池中的最小空闲连接数默认 0poolConfig.setMinIdle(minIdle);// 启用等待连接变为可用poolConfig.setBlockWhenExhausted(true);// 等待连接变为可用的最大秒数jdk17 以上支持低版本可用 setMaxWaitMillispoolConfig.setMaxWait(Duration.ofSeconds(1));// 控制检查池中空闲连接的间隔时间jdk17 以上支持低版本可用 setTimeBetweenEvictionRunsMillispoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));return new JedisPooled(poolConfig, new URI(redisUri));
} 3.4 Text Embedding
3.4.1 调用百炼 Embedding Model
使用百炼 Embedding 模型实现将文本转成向量数组并且自定义了使用的 模型和向量维度等参数。 public ListEmbedding textEmbedding(ListString texts) {// 调用百炼 Embedding 模型EmbeddingResponse embeddingResponse dashScopeEmbeddingModel.call(new EmbeddingRequest(texts,DashScopeEmbeddingOptions.builder().withModel(DashScopeApi.EmbeddingModel.EMBEDDING_V3.getValue()) // 设置使用的模型.withTextType(DashScopeApi.DEFAULT_EMBEDDING_TEXT_TYPE) // 设置文本类型.withDimensions(1024) // 设置向量维度可选768、1024、1536.build()));ListEmbedding results embeddingResponse.getResults();// 打印向量
// if (CollectionUtils.isEmpty(results)) {
// int tempSize results.size();
// for (int i 0; i tempSize; i) {
// float[] embeddingValue results.get(i).getOutput();
// log.info(embeddingValue:{}, embeddingValue.toString());
// }
// }return results;} 3.5 向量数据入库
3.5.1 主要代码
向量数据入库存入 Hash 类型数据。 Autowired
private RedisTemplate redisTemplate;/*** 存入向量信息** param key redis KEY* param vectorField 向量存储字段名* param vectorValue 向量值* param contentField 向量文本内容存储字段名* param content 向量文本内容增加存储文本内容方便查看向量的内容*/
public void addDocument(String key, String vectorField, float[] vectorValue, String contentField, String content) {// 将向量值转为二进制byte[] vectorByte CommonUtil.floatArrayToByteArray(vectorValue);// 组装字段mapMapString, Object fieldMap new LinkedMap();fieldMap.put(contentField, content.getBytes(StandardCharsets.UTF_8));fieldMap.put(vectorField, vectorByte);// 入库redisTemplate.opsForHash().putAll(key, fieldMap);
} 3.5.2 存储数据展示
可以看到存储的数据类型为 Hash 其中 vector_filed 为存储的向量值 content 为向量文本内容。 3.6 查询相似度
3.6.1 主要代码
执行 FT.SEARCH 查询相似度基于 KNN 算法k 近邻算法并且使用余弦相似度度量法作为 KNN 的距离度量方式。 Autowired
private JedisPooled jedisPooled;/*** 相似度搜索** param queryVector 查询的向量内容* param k 返回 k 个最相似内容* param indexName 索引名称* param vectorField 向量存储字段名* return*/
public SearchResult similaritySearch(float[] queryVector, int k, String indexName, String vectorField) {// 组装查询// 同FT.SEARCH embedding_index * [KNN 3 vector_field $query_vector AS distance]// PARAMS 2 query_vector \x12\xa9\xf5\x6c// SORTBY distance// DIALECT 4Query q new Query(*[KNN $K vectorField $query_vector AS distance]).returnFields(content, distance).addParam(K, k).addParam(query_vector, CommonUtil.floatArrayToByteArray(queryVector)).setSortBy(distance, true).dialect(4);// 使用 UnifiedJedis 执行 FT.SEARCH 语句try {SearchResult result jedisPooled.ftSearch(indexName, q);log.info(redis相似度搜索result:{}, JSONObject.toJSONString(result));return result;}catch (Exception e) {log.warn(redis相似度搜索异常:, e);throw new ErrorCodeException(e.getMessage());}
} 3.6.2 查询结果展示
其中 distance 为两向量的距离。 3.7 工具方法
3.7.1 float[] 转二进制 /*** float[] 转二进制** param floats* return*/
public static byte[] floatArrayToByteArray(float[] floats) {byte[] bytes new byte[Float.BYTES * floats.length];ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().put(floats);return bytes;
} 4 SpringAI VectorStore 实现
Spring AI VectorStore 的实现跟我上面的实现逻辑差不多只不过 Spring AI 将向量入库和查询都封装成通用的减少代码量并且其它向量库的 jar 也都实现了上述接口可已更方便快捷的切换向量库。 如果配置文件中 initialize-schema 值为 true索引将在配置初始化的时候自动被创建。 其代码位置 RedisVectorStore. afterPropertiesSet可以看到其创建的索引数据类型为 JSON使用余弦计算相似度下面的 schemaFields() 在我的另一篇文章 Redis 作为向量库入门指南 中有介绍。 4.1 引入依赖
4.1.1 引入 Spring AI Alibaba
我使用的版本是 1.0.0-M3.2对应 Spring AI 的版本为 1.0.0-M3。 dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter/artifactId
/dependency 4.1.2 引入 Spring Ai Redis Store
这是 SpringAI 中 Redis 作为 VectorStore 的包由 spring 提供跟 SpringAI 版本相同。 dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-redis-store-spring-boot-starter/artifactId
/dependency 4.2 添加配置
4.2.1 添加 Spring Ai Alibaba 配置
application.yml 中添加 spring:ai:dashscope:# 阿里百炼平台申请的 api_key --api-key: your api_keychat:client:enabled: true 4.2.2 添加 Spring AI Redis Store 配置
这里我使用了手动配置与 Spring AI 中 自动配置的参数稍有区别请甄别 Spring AI Redis VectorStore 使用 application.yml 中添加 spring:data:redis:# uri 为 spring ai 中使用 redis 作为 Vector Store 的配置uri: redis://:gusy1234127.0.0.1:6379/0ai:# 这里我多包了一层 mine ,因为如果多加一层spring 会自动装配mine:# redis 作为向量库的配置 vectorstore:redis:# 是否初始化索引信息initialize-schema: true# 索引名称默认 spring-ai-indexindex-name: spring-ai-index# 向量字段 key 的前缀默认 embedding:prefix: embedding-ai:# 文档批处理计算嵌入的策略。选项为 TOKEN_COUNT 或 FIXED_SIZEbatching-strategy: TOKEN_COUNT 4.3 配置 JedisPooled 连接池
同 3.3.2 。 4.4 配置 VectorSotre
新增 VectorStoreConfig 配置类 VectorSotre 配置之前你需要先配置 EmbeddingModel 和 JedisPooled 。 注官网文档中的代码已过时这是最新的实现方式。 Resourceprivate DashScopeEmbeddingModel dashScopeEmbeddingModel;Resourceprivate JedisPooled jedisPooled;Bean(vectorStoreWithDashScope)public VectorStore vectorStore() {return new RedisVectorStore(RedisVectorStore.RedisVectorStoreConfig.builder().withIndexName(indexName).withPrefix(prefix)
// .withEmbeddingFieldName(embedding) // 向量字段名默认 embedding
// .withContentFieldName(content) // 文本字段名默认 content.build(), embeddingModel, jedisPooled, initializeSchema); // initializeSchema 为是否初始化索引配置信息} 4.5 向量数据入库
4.5.1 主要代码 Autowired
Qualifier(vectorStoreWithDashScope)
private VectorStore vectorStore;// 向量入库主要代码
ListString inputInfos List.of(文本1,文本2);
ListDocument documentList new ArrayList();
inputInfos.forEach(text - {documentList.add(new Document(text));
});
vectorStore.add(documentList); 4.5.2 存储数据展示
可以看到 Spring AI 存储的向量为 JSON 格式其 Redis 库中的内容如下其中 embedding 为向量内容 content 为向量文本 4.6 查询相似度
4.6.1 主要代码
可以看到代码实现很简单参数也都是熟悉的这里不再赘述。 // 相似度查询主要代码
ListDocument result vectorStore.similaritySearch(org.springframework.ai.vectorstore.SearchRequest.defaults().withQuery(inputInfos.get(0)) // 查询的内容.withTopK(3)// 一个值从 0 到 1 的双精度数接近1的值表示更高的相似性。默认为为0.75。.withSimilarityThreshold(0.6)
); 4.6.2 查询结果展示 5. 参考文档 Java 中使用 Jedis 客户端库执行向量搜索Spring AI Redis Vector Database 文档Spring AI Alibaba 向量存储文档 Jedis 官方教程