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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 可能是全网第一个使用RediSearch实战的项目

实战项目地址newbeemall,集成RediSearch,代码开源已上传,支持的话可以点个star😁RediSearch 是基于 Redis 开发的支持二级索引、查询引擎和全文搜索的应用程序。在2.0的版本中,简单看下官网测试报告:

索引构建

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

image.png

查询性能

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

image.png

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

image.png

一、RediSearch安装

Docker安装最新版

docker run -p 6379:6379 redislabs/redisearch:latest

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

1、redis-cli -h localhost 
2、module list
82.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客户端
@Bean
public 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创建索引
@Test
public 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>

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

image.png

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

// 同步商品到RediSearch
public 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. 查看搜索结果中包含"小米"、"手机"两个单独分词
  2. image.png

四、总结

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

image.png

高级用法聚合查询、结果高亮、停用词、扩展API、拼写更正、自动补全等可以在官网了解。

最后贴一下实战项目地址newbeemall,集成RediSearch,代码开源已上传

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4月前
|
数据采集 存储 数据库
构建你的第一个Python爬虫:从入门到实践
【8月更文挑战第31天】在数字时代的浪潮中,数据如同新时代的石油,而网络爬虫则是开采这些数据的钻头。本文将引导初学者了解并实现一个基础的网络爬虫,使用Python语言,通过实际代码示例,展示如何收集和解析网页信息。我们将一起探索HTTP请求、HTML解析以及数据存储等核心概念,让你能够快速上手并运行你的首个爬虫项目。
|
4月前
|
图形学
VRTK4⭐二.VRTK4的项目基础配置
VRTK4⭐二.VRTK4的项目基础配置
|
7月前
|
监控 安全 Java
腾讯T4大牛整理的SpringBoot文档,覆盖你认知中的所有操作
SpringBoot目前的使用已经很普遍了,实际的项目中,我们需要集成各种的插件支持,不仅如此,还有很多可能我们平时不知道,但是很方便的操作。pdf里面的东西还是比较全面的。
|
前端开发
前端知识案例1-依赖收集1-问题解决 原
前端知识案例1-依赖收集1-问题解决 原
60 0
前端知识案例1-依赖收集1-问题解决 原
号外!Rmarkdown教程全网发布
寒假期间花了大量时间(100h+)学习和制作了Rmarkdown入门教程,昨晚终于录制和剪辑完毕
153 0
|
JSON 前端开发 数据可视化
一文了解文件上传全过程(1.8w字深度解析,进阶必备)
由于文件上传功能将使许多应用程序受益,因此建议对HTML进行扩展,以允许信息提供者统一表达文件上传请求,并提供文件上传响应的MIME兼容表示。
1020 0
一文了解文件上传全过程(1.8w字深度解析,进阶必备)
|
数据采集
我用加强版RFM模型,轻松扒出B站优质up主!(含数据+实战代码)(上)
本文在RFM模型基础上做了调整,尝试用更符合b站特性的IFL模型,找到各分区优质up主。整个过程以分析项目的形式展开,最终附上了完整源数据和代码,方便感兴趣的同学练手。
329 0
我用加强版RFM模型,轻松扒出B站优质up主!(含数据+实战代码)(上)
我用加强版RFM模型,轻松扒出B站优质up主!(含数据+实战代码)(中)
本文在RFM模型基础上做了调整,尝试用更符合b站特性的IFL模型,找到各分区优质up主。整个过程以分析项目的形式展开,最终附上了完整源数据和代码,方便感兴趣的同学练手。
212 0
我用加强版RFM模型,轻松扒出B站优质up主!(含数据+实战代码)(中)
|
SQL 测试技术 数据安全/隐私保护
测试从零开始-电商项目实战-功能实战篇No.1-[后台-用户列表]
在之前的文章中,已经介绍过,如何去设计测试用例,并且以一个开源电商项目的后台某个模块去分析了一些比较常见的测试点,那么,今天将针对这个模块进行功能测试,看一下在测试过程中,我们能发现一些什么样的问题。
下一篇
DataWorks