写点什么

可能是全网第一个使用 RediSearch 实战的项目

  • 2022 年 4 月 06 日
  • 本文字数:3487 字

    阅读完需:约 11 分钟

可能是全网第一个使用RediSearch实战的项目

实战项目地址newbeemall,集成 RediSearch,代码开源已上传,支持的话可以点个 star😁


RediSearch 是基于 Redis 开发的支持二级索引、查询引擎和全文搜索的应用程序。在 2.0 的版本中,简单看下官网测试报告:

索引构建

在索引构建测试中,RediSearch 用 221 秒的速度超过了 Elasticsearch 的 349 秒,领先 58%,


查询性能

数据集建立索引后,我们使用运行在专用负载生成器服务器上的 32 个客户端启动了两个词的搜索查询。如下图所示,RediSearch 的吞吐量达到了 12.5K ops/sec,而 Elasticsearch 的吞吐量达到了 3.1K ops/sec,快了 4 倍。此外,RediSearch 的延迟稍好一些,平均为 8 毫秒,而 Elasticsearch 为 10 毫秒。(ops/sec 每秒操作数)



由此可见,新的 RediSearch 在性能上对比 RediSearch 较有优势,此外对中文项目来说对于中文的支持必不可少,RediSearch 也在官网文档特意列出了支持中文,基于frisoC 语言开发的中文分词项目。


一、RediSearch 安装

Docker 安装最新版

docker run -p 6379:6379 redislabs/redisearch:latest
复制代码


通过 redis-cli 连接查看 RediSearch 是否安装成功

1、redis-cli -h localhost 2、module list82.157.141.70:16789> MODULE LIST 
1) 1) "name" 2) "search" # 查看是否包含search模块 3) "ver" 4) (integer) 20210 2) 1) "name" 2) "ReJSON" # 查看是否包含ReJSON模块 3) "ver" 4) (integer) 20007
复制代码

二、客户端集成

对于 Java 项目直接选用 Jedis4.0 版本就可以,Jedis 在 4.0 版本自动支持 RediSearch,编写 Jedis 连接 RedisSearch 测试用例,用 RedisSearch 命令创建如下:

FT.CREATE idx:goods on hash prefix 1 "goods:" language chinese schema goodsName text sortable// FT.CREATE 创建索引命令// idx:goods 索引名称// on hash 索引数据基于hash类型源数据构建// prefix 1 "goods:" 表示要创建索引的源数据前缀匹配规则// language chinese 表示支持中文语言分词// schema 表示字段定义,goodsName元数据属性名 text字段类型 sortable自持排序
FT.INFO idx:goods // FT.INFO 查询指定名称索引信息
FT.DROPINDEX idx:goods // FT.DROPINDEX 删除指定名称索引,不会删除源数据
添加索引时,使用hset命令添加索引源数据删除索引时,使用del命令删除索引源数据
复制代码


  1. Jedis 创建 RediSearch 客户端

@Beanpublic UnifiedJedis unifiedJedis(GenericObjectPoolConfig jedisPoolConfig) {    UnifiedJedis client;    if (StringUtils.isNotEmpty(password)) {        client = new JedisPooled(jedisPoolConfig, host, port, timeout, password, database);    } else {        client = new JedisPooled(jedisPoolConfig, host, port, timeout, null, database);    }    return client;}
复制代码


  1. Jedis 创建索引

@Testpublic void createIndex() {    System.out.println("begin");    Schema schema = new Schema()            .addSortableTextField("goodsName", 1.0)            .addSortableTextField("goodsIntro", 0.5)            .addSortableTagField("tag", "|");    jedisSearch.createIndex(idxName, "goods", schema);    System.out.println("end");}
/** * 创建索引 * * @param idxName 索引名称 * @param prefix 要索引的数据前缀 * @param schema 索引字段配置 */public void createIndex(String idxName, String prefix, Schema schema) { IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.HASH) .setPrefixes(prefix) .setLanguage(Constants.GOODS_IDX_LANGUAGE); # 设置支持中文分词 client.ftCreate(idxName, IndexOptions.defaultOptions().setDefinition(rule), schema);}
复制代码


  1. Jedis 添加索引源数据

/** * 添加索引数据 * * @param keyPrefix 要索引的数据前缀 * @param goods     商品信息 * @return boolean */public boolean addGoodsIndex(String keyPrefix, Goods goods) {    Map<String, String> hash = MyBeanUtil.toMap(goods);    hash.put("_language", Constants.GOODS_IDX_LANGUAGE);    client.hset(keyPrefix + goods.getGoodsId(), MyBeanUtil.toMap(goods));    return true;}
复制代码


  1. Jedis 中文查询

public SearchResult search(String goodsIdxName, SearchObjVO searchObjVO,     Page<SearchPageGoodsVO> page) {    String keyword = searchObjVO.getKeyword(); // 查询关键字    String queryKey = String.format("@goodsName:(%s)", keyword);    Query q = new Query(queryKey);    String sort = searchObjVO.getSidx();    String order = searchObjVO.getOrder();    // 查询是否排序    if (StringUtils.isNotBlank(sort)) {        q.setSortBy(sort, Constants.SORT_ASC.equals(order));
} // 设置中文分词查询 q.setLanguage(Constants.GOODS_IDX_LANGUAGE); // 查询分页 q.limit((int) page.offset(), (int) page.getSize()); // 返回查询结果 return client.ftSearch(goodsIdxName, q);}
复制代码

三、项目实战

  1. 引入 Jedis4.0

<jedis.version>4.2.0</jedis.version><!-- jedis --><dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>${jedis.version}</version></dependency>
复制代码


  1. newbeemall项目后台商品管理中添加同步按钮


商品全量同步按钮,为了加快同步速度,通过多线程同步

// 同步商品到RediSearchpublic boolean syncRs() {    jedisSearch.dropIndex(Constants.GOODS_IDX_NAME);    Schema schema = new Schema()            .addSortableTextField("goodsName", 1.0)            .addSortableTextField("goodsIntro", 0.5)            .addSortableNumericField("goodsId")            .addSortableNumericField("sellingPrice")            .addSortableNumericField("originalPrice")            .addSortableTagField("tag", "|");    jedisSearch.createIndex(Constants.GOODS_IDX_NAME, "goods:", schema);    List<Goods> list = this.list();    jedisSearch.deleteGoodsList(Constants.GOODS_IDX_PREFIX);    return jedisSearch.addGoodsListIndex(Constants.GOODS_IDX_PREFIX, list);}/** * 同步商品索引 * * @param keyPrefix 要索引的数据前缀 * @return boolean */public boolean addGoodsListIndex(String keyPrefix, List<Goods> list) {    int chunk = 200;    int size = list.size();    int ceil = (int) Math.ceil(size / (double) chunk);    // 多线程同步    List<CompletableFuture<Void>> futures = new ArrayList<>(4);    for (int i = 0; i < ceil; i++) {        int toIndex = (i + 1) * chunk;        if (toIndex > size) {            toIndex = i * chunk + size % chunk;        }        List<Goods> subList = list.subList(i * chunk, toIndex);        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> subList).thenAccept(goodsList -> {            for (Goods goods : goodsList) {                Map<String, String> hash = MyBeanUtil.toMap(goods);                hash.put("_language", Constants.GOODS_IDX_LANGUAGE);                client.hset(keyPrefix + goods.getGoodsId(), MyBeanUtil.toMap(goods));            }        });        futures.add(voidCompletableFuture);    }    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();    return true;}
复制代码


  1. 修改商品页面搜索接口

@GetMapping("/search")public String rsRearch(SearchObjVO searchObjVO, HttpServletRequest request) {    Page<SearchPageGoodsVO> page = getPage(request, Constants.GOODS_SEARCH_PAGE_LIMIT);    ...    // RediSearch中文搜索    SearchResult query = jedisSearch.search(Constants.GOODS_IDX_NAME, searchObjVO, page);    ...    return "mall/search";}
复制代码


  1. 查看搜索结果中包含"小米"、"手机"两个单独分词

四、总结

以上实战项目,使用RediSearch是可以满足基本中文分词需求


高级用法聚合查询、结果高亮、停用词、扩展 API、拼写更正、自动补全等可以在官网了解。最后贴一下实战项目地址newbeemall,集成 RediSearch,代码开源已上传

发布于: 刚刚阅读数: 2
用户头像

waynaqua 2020.03.10 加入

java开发工程师

评论

发布
暂无评论
可能是全网第一个使用RediSearch实战的项目_redis_越长大越悲伤_InfoQ写作平台