在 2021 年我就了解到 RediSearch 这个项目,并现已把它用于我的开源项目 newbee-mall-pro 中。

就我的运用体会来说,简单场景下,用来平替 Elasticsearch 的运用场景现已满足。像是 Elasticsearch 中常用中文分词插件可以用 RediSearch 代替,可是拼音转中文插件在 RediSearch 中还没有功能代替,只能经过个人手法处理。

在 newbee-mall-pro 项目中,拼音查找我是经过先将中文转拼音后作为拼音字段存入 Redis 中,再经过 RediSearch 查询拼音字段来完成的。

RediSearch 关于我来说比较 Elasticsearch 的最大长处便是 内存占用非常低,查询性能也满足高

在我的低配 2 核 4g 内存的服务器上,经过官方供给的 Redis Stack 镜像布置 Redis 以及自带模块 RediSearch 后,内存占用才不到 100m。

比较布置一个 Elasticsearch 最少需求 1g 内存来说,我更愿意布置 RediSearch。本文纲要如下,

Redis 也支撑全文查找 了?这也太强了

RediSearch 简介

RediSearch 是一个 Redis 模块,为 Redis 供给查询、二级索引和全文查找功能。

要运用 RediSearch 的功能,咱们需求要先声明一个 index(类似于 Elasticsearch 的索引)。然后就可以运用 RediSearch 的查询言语来查询该索引下的数据。

RediSearch 内部运用压缩的倒排索引,所以可以已较低的内存占用来完成索引的快速构建。

现在 RediSearch 最新版支撑的查询功能也比较丰富了,除了根本的文本分词还支撑聚合统计、停用词、同义词、拼写查看、结果排序、标签查询、向量相似度查询以及中文分词等。

比照 Elasticsearch

根本硬件

Redis 也支撑全文查找 了?这也太强了

数据源

Redis 也支撑全文查找 了?这也太强了

RediSearch 装备

Redis 也支撑全文查找 了?这也太强了

Elasticsearch 装备

Redis 也支撑全文查找 了?这也太强了

版别

Redis 也支撑全文查找 了?这也太强了

索引构建测验

在官方供给的索引构建测验中,RediSearch 用 221 秒的速度超过了 Elasticsearch 的 349 秒,抢先 58%,

Redis 也支撑全文查找 了?这也太强了

查询性能测验

经过数据集导入索引数据后,官方运用运行在专用负载生成器服务器上的 32 个客户端启动了两个词的查找查询。

如下图所示,RediSearch 的吞吐量达到了 12.5K ops/sec,而 Elasticsearch 的吞吐量只要了 3.1K ops/sec,快了 4 倍。此外 RediSearch 的推迟稍好一些,平均为 8 毫秒,而 Elasticsearch 为 10 毫秒。 (ops/sec 每秒操作数

Redis 也支撑全文查找 了?这也太强了

由此可见,RediSearch 在性能上比照 RediSearch 有比较大的优势。


现在 RediSearch 现已更新到 2.0+ 版别,根据官方关于 RediSearch 2.0 版别介绍,与 RediSearch 1.6 比较,吞吐量和推迟相关的目标都提高了 2.4 倍。

RediSearch 装置

关于现在最新的 RediSearch 2.0 版别来说,官方引荐直接运用 redis-stack-server 镜像进行进行布置,也比较简单,

docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest

设置登录密码

// 设置登录密码
docker run -e REDIS_ARGS="--requirepass redis-stack" redis/redis-stack:latest

经过 redis-cli 衔接查看 RediSearch 是否装置了 search 模块,

redis-cli -h localhost
> MODULE list
...
3) 1) "name"
   2) "search"
   3) "ver"
   4) "20809"
   5) "path"
   6) "/opt/redis-stack/lib/redisearch.so"
   7) "args"
   8) 1) "MAXSEARCHRESULTS"
      2) "10000"
      3) "MAXAGGREGATERESULTS"
      4) "10000"
...

索引操作

FT.CREATE 创立索引指令

> FT.CREATE idx:goods on hash prefix 1 "goods:" language chinese schema goodsName text sortable
"OK"
  • FT.CREATE:创立索引指令
  • idx:goods:索引称号
  • on hash:索引关联的数据类型,这里指定索引基于 hash 类型的源数据构建
  • prefix 1 “goods:”:表明索引关联的 hash 类型源数据前缀是 goods:
  • language chinese:表明支撑中文言语分词
  • schema goodsName text sortable:表明字段界说,goodsName 表明元数据属性名,text 表明字段类型 sortable 表明该字段可以用于排序

增加索引时,直接运用 hset 指令增加一个 key 前缀是 “goods:” 的源数据。如下,

hset goods:1001 goodsName 小米手机
hset goods:1002 goodsName 华为手机

FT.SEARCH 查询索引

> FT.SEARCH idx:goods1 "手机"
1) "2"
2) "goods:1001"
3) 1) "goodsName"
   2) "xe5xb0x8fxe7xb1xb3xe6x89x8bxe6x9cxba"
4) "goods:1002"
5) 1) "goodsName"
   2) "xe5x8dx8exe4xb8xbaxe6x89x8bxe6x9cxba"

FT.INFO 查询指定称号索引信息

> FT.INFO idx:goods
1) "index_name"
2) "idx:goods1"
3) "index_options"
4) (empty list or set)
5) "index_definition"
6) 1) "key_type"
   2) "HASH"
   3) "prefixes"
   4) 1) "goods:"
   5) "default_language"
   6) "chinese"
   7) "default_score"
   8) "1"
7) "attributes"
8) 1) 1) "identifier"
      2) "goodsName"
      3) "attribute"
      4) "goodsName"
      5) "type"
      6) "TEXT"
      7) "WEIGHT"
      8) "1"
      9) "SORTABLE"
...
  • FT.INFO 查询指定称号的索引信息

FT.DROPINDEX 删去索引称号

> FT.DROPINDEX idx:goods1
"OK"
  • FT.DROPINDEX 删去指定称号索引,不会删去 hash 类型的源数据

假如需求删去索引数据,直接运用 del 指令删去索引关联的源数据即可。

Java 运用 RediSearch

关于 Java 项目直接选用 Jedis4.0 以上版别就可以运用 RediSearch 供给的查找功能,Jedis 在 4.0 以上版别主动支撑 RediSearch,编写 Jedis 衔接 RedisSearch 测验用例,代码如下,

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;
}

Jedis 创立索引

Schema schema = new Schema()
        .addSortableTextField("goodsName", 1.0)
        .addSortableTagField("tag", "|");
IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.HASH)
        .setPrefixes("idx:goods")
        .setLanguage("chinese"); # 设置支撑中文分词
client.ftCreate(idxName,
        IndexOptions.defaultOptions().setDefinition(rule),
        schema);

Jedis 增加索引源数据

public boolean addGoodsIndex(String keyPrefix, Goods goods) {
    Map<String, String> hash = MyBeanUtil.toMap(goods);
    hash.put("_language", "chinese");
    client.hset("idx:goods" + goods.getGoodsId(), MyBeanUtil.toMap(goods));
    return true;
}

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("chinese");
    // 设置分页
    q.limit((int) page.offset(), (int) page.getSize());
    // 返回查询结果
    return client.ftSearch(goodsIdxName, q);
}

最终聊两句

RediSearch 是这几年新出的一个全文查找引擎,借助于 Redis 的成功,RediSearch 一出场就获得了较高的重视度。

现在来看,我个人运用 RediSearch 作为 newbee-mall-pro 项目的全文查找引擎现已够用了,它有易于装置、索引占用内存低、查询速度快等许多长处。不过再对 Redis 集群的支撑上,RediSearch 现在只针对 Redis 企业版有解决方案,开源版还没有,这一点需求告知大家。

假如想要在生产环境大规模运用,我仍是不太主张的。

最终本文运用的 Jedis 操作 RediSearch 相关代码,都在 newbee-mall-pro 项目的 JedisSearchTest 类有体现。

newbee-mall-pro项目地址:github.com/wayn111/new…

重视公众号【waynblog】,每周分享技术干货、开源项目、实战经验、高效开发工具等,您的重视将是我的更新动力。