dot 快速的未来即将在您所在的城市举行活动。

加入我们参加 Redis 发布会

在 Redis 中添加搜索引擎:模块领域的冒险

简而言之:我们利用功能强大的 Redis 模块 API 从头开始构建了一个超级快速且功能丰富的搜索引擎,它拥有自己的自定义数据类型和算法。

今年早些时候,Redis 的作者 Salvatore Sanfilippo 开始开发一种方法,使开发人员能够使用新的功能扩展 Redis:Redis 模块 API。此 API 允许用户使用原生 C 编写代码,向 Redis 添加新命令和数据类型。在 Redis,我们很快开始使用模块进行实验,将 Redis 带到前所未有的新领域。

我们探索的一个超级激动人心但不太简单的案例是构建搜索引擎。Redis 已经通过客户端库被许多用户用于搜索(我们甚至发布了自己的搜索客户端库)。但是,我们知道 Redis 模块的力量将使搜索功能和性能能够实现这些库无法提供的功能。

因此,我们开始开发一个有趣的搜索引擎实现,它使用 C 从头开始构建,并使用 Redis 作为框架。结果,RediSearch 现在作为一个开源项目提供。

其主要功能包括

  • 简单、超快(参见下面的基准测试)的索引和搜索,都在内存中
  • 自定义用户定义的文档和字段排名
  • 对结果进行数值过滤
  • 支持词干提取
  • 功能强大的自动建议引擎,具有模糊搜索功能
  • … 等等!

品尝一下:RediSearch 的实际应用

让我们使用这个使用 redis-cli 工具的示例来看看 RediSearch 的一些关键概念

  1. 定义索引

    使用 RediSearch 的第一步是创建索引定义,它告诉 RediSearch 如何处理我们将添加的文档。

    我们使用 FT.CREATE 命令,其语法如下

    FT.CREATE <index_name> <field> [<score>|NUMERIC] …

    或者在我们这个例子中

    FT.CREATE products name 10.0 description 1.0 price NUMERIC

    这意味着 namedescription 被视为文本字段,分别具有 10 和 1 的分数,而 price 是用于过滤的数值字段。

  2. 添加文档

    现在,我们可以使用 FT.ADD 命令将新产品添加到我们的索引中

    FT.ADD <index_name> <doc_id> <score> FIELDS <field> <value> …

    添加产品

    FT.ADD products prod1 1.0 FIELDS
    name “Acme 40 英寸 1080p LED 电视”
    description “享受增强色彩和清晰度的惊人全高清 1080p”
    price 277.99

    该产品会立即添加到索引中,现在可以在将来的搜索中找到。

  3. 搜索

    现在我们已经将产品添加到索引中,搜索非常简单

    FT.SEARCH products “full hd tv” LIMIT 0 10

    或使用价格过滤,介于 200 美元和 300 美元之间

    FT.SEARCH products “full hd tv” FILTER price 200 300

    结果在 redis-cli 中显示为

    1) (integer) 1
    2) “prod1”
    3) 1) “name”
    2) “Acme 40 英寸 1080p LED 电视”
    3) “description”
    4) “享受增强色彩和清晰度的惊人全高清 1080p,分辨率是标准高清电视的两倍”
    5) “price”
    6) “277.99”

设计:偏离 Redis 的数据结构

我们在 RediSearch 中的第一个设计选择是使用自定义数据结构对 倒排索引 进行建模。这允许更有效地编码数据,并且可能更快地遍历和交叉索引。我们选择简单地使用 Redis 字符串键作为二进制编码索引,并在搜索时直接从内存中读取它们,这种技术在模块 API 中被称为字符串 DMA。另一方面,我们使用普通的旧 redis 哈希对象保存文档内容。

我们使用结合了 增量编码Varint 编码 的技术在 Redis 中保存倒排索引,以对条目进行编码,从而最大限度地减少索引使用的空间,同时保持解压缩和遍历的效率。

Storing document content and index data on different redis data types

自动完成和模糊建议

RediSearch 的另一个重要功能是其自动完成或建议命令。这允许您创建加权术语字典,然后查询它们以获得对给定用户前缀的完成建议。例如,如果用户开始将术语“lcd tv”输入字典,则发送前缀“lc”将返回完整术语作为结果。

RediSearch 还允许模糊建议,这意味着即使用户在其前缀中输入了错误,您也可以获得对前缀的建议。这是通过使用 莱文斯坦自动机 实现的,允许高效地在字典中搜索所有术语,这些术语的 莱文斯坦距离 与某个术语或前缀的最大距离不超过某个值。

以下是在 RediSearch 中创建和搜索自动完成字典的示例

  1. 将一些术语及其权重添加到名为“completer”的字典中(注意返回值是插入后字典的大小)

    127.0.0.1:6379> FT.SUGADD completer “foo” 1
    (integer) 1 127.0.0.1:6379> FT.SUGADD completer “football” 5
    (integer) 2 127.0.0.1:6379> FT.SUGADD completer “fawlty towers” 7
    (integer) 3 127.0.0.1:6379> FT.SUGADD completer “foo bar” 2
    (integer) 4 127.0.0.1:6379> FT.SUGADD completer “fortune 500” 1
    (integer) 5

  2. 简单的前缀搜索,包括分数

    127.0.0.1:6379> FT.SUGGET completer foo
    1) “football”
    2) “foo”
    3) “foo bar”

  3. 模糊前缀搜索(注意我们得到的结果其前缀与给定前缀的莱文斯坦距离最多为 1)

    127.0.0.1:6379> FT.SUGGET completer foo FUZZY
    1) “football”
    2) “foo”
    3) “foo bar”
    4) “fortune 500”

    127.0.0.1:6379> FT.SUGGET completer faulty FUZZY
    1) “fawlty towers”

一些基准测试结果

为了评估 RediSearch 与其他开源搜索引擎的性能,我们创建了一组基准测试,测量延迟和吞吐量,同时对相同的数据集进行索引和查询。

以下是一些结果,显示 RediSearch 以显著优势优于其他开源搜索引擎(当然,与往常一样,基准测试结果应该谨慎对待。 查看链接的白皮书 以获取更多结果和关于基准测试的信息)

Benchmark 1: Easy single-word query - hello
Benchmark 1: Easy single-word query - hello

基准测试 1:简单的单字查询 - hello

Benchmark 2: two word query - barack obama
Benchmark 2: two word query - barack obama

基准测试 2:两个单词的查询 - barack obama

Benchmark 3: Autocomplete - 1100 top 2-3 letter prefixes in Wikipedia
Benchmark 3: Autocomplete - 1100 top 2-3 letter prefixes in Wikipedia

基准测试 3:自动完成 - 维基百科中排名前 1100 的 2-3 个字母的前缀

更多信息

  • 我们发布的白皮书中对 RediSearch 及其内部机制进行了更全面的深入分析。 可以在这里找到.
  • RediSearch 的 源代码在 Github 上提供,根据 AGPL 许可。欢迎您试用它,并与我们合作,使它变得更好!