Redis 也支援全文搜尋?這也太強了

張哥說技術發表於2023-12-13

來源:waynblog

在 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) "\xe5\xb0\x8f\xe7\xb1\xb3\xe6\x89\x8b\xe6\x9c\xba"
4) "goods:1002"
5) 1) "goodsName"
   2) "\xe5\x8d\x8e\xe4\xb8\xba\xe6\x89\x8b\xe6\x9c\xba"

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 測試用例,用 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:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2999904/,如需轉載,請註明出處,否則將追究法律責任。

相關文章