我最近一直在使用 RediSearch 模块,它是在 Redis 生态系统中最令人着迷的发展之一,值得拥有自己的系列文章。
如果您构建了一个以 Redis 作为主要数据存储的应用程序,那么您可能已经体验过原生数据类型的喜悦和困惑。当您了解数据类型时,您会意识到您的许多数据都非常适合其中之一。但是,许多常见的应用程序模式都需要索引(“哪个键具有 x 值?”)和搜索(“哪个键包含 一些文本字符串?”)。虽然可以通过创造性地利用原生数据类型来回答这些问题,但代码可能很复杂,并且存在速度和/或空间效率方面的权衡。RediSearch 模块弥补了这些空白,并且几乎没有权衡。在本期内容中,我们将探索该模块的最基本内容,作为一种简短的介绍。
模块是您的 Redis 服务器的附加组件。在最基本的层面上,它们实现新的命令,但它们也可以实现新的数据类型。模块是用系统编程语言编写的;C/C++、Rust 和 Golang 已经使用过,但其他语言也是可能的。由于它们是用编译语言编写的,因此可以实现极高的性能。
模块不同于 Redis 脚本(Lua),因为它们是系统中的一级命令,可以与存储直接交互,从而能够创建自己的数据类型。它们与内置命令唯一的区别是,模块命令使用前缀命名空间,通常是两个字母和一个点(例如:XX.SOMECOMMAND)。
模块可以通过 MODULE LOAD 随时加载,通过 redis.conf 文件中的 loadmodule 加载,或通过命令行参数“loadmodule”加载。我个人更倾向于通过配置文件加载它们,因为这可以确保它们始终可用,并且配置是可移植的。
我一直在问自己 RediSearch 不是 什么 - 但我将尝试回答这个问题,而不会将其颠倒过来。RediSearch 是一个提供三个主要功能的模块
RediSearch 利用其自己的数据类型和内置的 Redis 数据类型。这样,它更像是一个使用 Redis 并与 Redis 共同驻留的解决方案。现在这可能看起来很混乱,但请耐心听我讲。
让我们从上述功能中评估每个功能。首先,考虑 全文搜索。使用 RediSearch,您可以索引尚未处理的文本。假设您有一百万条客户评论的列表,您想要找到所有提及“渲染”的评论。在 RediSearch 之前,您当然可以将这些评论存储在 Redis 中(例如,在哈希中),但是查找这些评论中的特定单词充其量是困难的。即使您设法构建了自己的词语到评论的索引(这涉及在应用程序级别将每个评论拆分为词语),匹配也需要是精确的 - “render”、“rendering”和“rendered”不会相互匹配。相反,通过使用 RediSearch 存储数据,您可以找到所有评论,而无需在应用程序级别执行任何特殊操作,它会自动将“rendered”与“rendering”匹配,因为它可以智能地处理索引和查询。
显然,如果可以执行上述操作,那么也可以在没有语言处理智能的情况下执行 - 当您开始思考这一点时,您开始意识到 RediSearch 可以用作通用二级索引。但是,也可以超越文本匹配 - RediSearch 可以对单个项目(称为“文档”)执行数字和地理索引。每个文档可以有多个字段 - 每个字段都有单独的属性。
最后,在某种程度上独立于上述内容,RediSearch 提供了一个建议引擎,可以驱动类似自动完成的服务。这使您可以获取已知的有效值,并为用户提供“提示”。它基于前缀模型,因此如果用户开始输入“Hamb”,建议引擎将提供“Hamburger”、“Hambone”和“Hamburg”等。重要的是要注意,这些建议没有直接与搜索结果集成,因此您的应用程序需要负责将它们添加或删除到该建议存储中。
作为实践练习,让我们安装该模块
$ git clone https://github.com/RedisLabsModules/RediSearch.git
$ cd RediSearch
$ make all
$ cd src
$ redis-cli
> MODULE LOAD ./redisearch.so
(或者将其安装在您的 redis.conf 文件中,然后重新启动 redis-server)
加载模块后,在 redis-cli 中运行以下命令以验证模块是否正在运行
> module list
1) 1) "name"
2) "ft"
3) "ver"
4) (integer) 2000
在该命令的结果中,您应该看到您安装的每个模块的条目(可能只有一个)。其中一个条目的“name”字段应该读作“ft”(表示 全文)。这就是 RediSearch 的识别方式和命令前缀。您的版本号可能与我的不同,该模块的开发速度很快。
现在该模块已启动并运行,最好为这些练习从一个干净的数据库开始(flushdb 或一个干净的数据库/实例)。首先,让我们创建一个索引并添加一个项目
> FT.CREATE shakespeare SCHEMA line TEXT SORTABLE play TEXT NOSTEM speech NUMERIC SORTABLE speaker TEXT NOSTEM entry TEXT location GEO
这可能看起来有点复杂,尤其是如果您习惯于使用 1 或 2 个参数的命令。让我们将其分解
FT.CREATE shakespeare
这只是命令和“键”(稍后将详细介绍)。
SCHEMA
这表示以下参数将与搜索索引中的字段有关。
line TEXT SORTABLE
在这里,我们创建了一个名为 line 的字段,它保存文本值,稍后将进行排序。
play TEXT NOSTEM
这是名为“play”的字段,用于保存文本值,但不会进行词干提取(例如,rendering 不会与 render 匹配)。
speech NUMERIC SORTABLE
我们创建了一个名为“speech”的字段,它是数值并且可以排序。
speaker TEXT NOSTEM
与 play 字段一样,speaker 字段将保存仅进行精确匹配(逐字匹配)的文本。
entry TEXT
该字段 (entry) 保存进行精确匹配或词干提取处理的文本值。
location GEO
location 字段保存地理坐标。
您看 - 只是在一行中包含很多内容,但实际上并不复杂。
现在,让我们将一个文档添加到我们的索引中
> FT.ADD shakespeare 57956 1 FIELDS text_entry "Out, damned spot! out, I say!--One: two: why," line "5.1.31" play macbeth speech 15 speaker "LADY MACBETH" location -3.9264,57.5243
比较这两个命令,您可能会注意到 FT.CREATE 和 FT.ADD 命令遵循类似的模式。让我们更深入地了解该命令
FT.ADD shakespeare 57956 1
我们将一个 ID 为 57956 的文档添加到索引 (shakespeare) 中。请注意,在此命令中,文档 ID 是一个数字(只是我使用的数据集的一个特征),但它可以是任何有效的 Redis 键。本节中的最后一个参数是权重 - 我们将在本系列的后面部分介绍它,但现在您只需要知道它可以介于 0 和 1 之间,而 1 是一个不错的默认值。
FIELDS …
“FIELDS”表示我们将以 [fieldname] [value] 重复模式指定文档的字段。请注意,当值为单个单词或数字时,您不需要使用引号,但如果您使用空格或其他奇特字符,请将您的值用引号括起来。另一个特殊的字段是位置字段,它包含一组坐标 (经度, 纬度)
回想一下,我们使用键“shakespeare”(通过 FT.CREATE 命令)创建了一个索引。让我们做一个快速实验
> TYPE shakespeare
none
很奇怪,对吧?从这里开始,我们将偏离正常的 Redis 行为,您将开始看到 RediSearch 是一种既使用 Redis 又与 Redis 集成的解决方案。
如果您在非生产数据库上运行此命令,让我们出于调试目的执行 KEYS *
> KEYS *
1) "ft:shakespeare/1"
2) "ft:shakespeare/31"
3) "idx:shakespeare"
4) "ft:shakespeare/5"
5) "ft:shakespeare/macbeth"
6) "ft:shakespeare/lady"
7) "nm:shakespeare/speech"
8) "geo:shakespeare/location"
9) "57956"
运行两个命令产生了 9 个键。我想要重点介绍其中一些键,以更全面地了解这里实际发生了什么
> TYPE idx:shakespeare
ft_index0
在这里,我们可以看到 RediSearch 使用其自己的数据类型创建了一个键(ft_index0)。我们无法直接对该键执行太多操作,但重要的是要知道它存在以及它是如何创建的。
现在,让我们看看键 57956
> TYPE 57956
hash
一个哈希!我们可以使用它 - 让我们直接查看该键
> HGETALL 57956
1) "text_entry"
2) "Out, damned spot! out, I say!--One: two: why,"
3) "line"
4) "5.1.31"
5) "play"
6) "macbeth"
7) "speech"
8) "15"
9) "speaker"
10) "LADY MACBETH"
11) "location"
12) "-3.9264,57.5243"
这看起来应该很熟悉,因为它是您来自 FT.ADD 命令的数据,密钥只是您的文档 ID。虽然了解它是如何存储的非常重要,但不要直接使用 HASH 命令来操作此密钥。
> TYPE nm:shakespeare/speech
numericdx
有趣的是,我们数据集中名为 speech 的字段是一个数字索引,类型为“numericdx”。同样,由于这是一个 RediSearch 原生数据类型,我们无法使用任何“正常”Redis 命令来操作它。
> TYPE geo:shakespeare/location
zset
这里的关键为您提供了一个提示,虽然 TYPE 命令返回它是一个 ZSET,但 Redis 地理哈希集存储为 ZSET,并且在查询类型时会报告为 ZSET。话虽如此,让我们看一下几个 GEO 命令。
> GEOHASH geo:shakespeare/location 1
1) "gfjpnxuzk40"
> GEOPOS geo:shakespeare/location 1
1) 1) "-3.92640262842178345"
2) "57.52429905544970268"
太棒了!RediSearch 已将坐标存储在一个标准的 GEO 集合中。但是,与上面的哈希一样,不要使用 ZSET 或 GEO 命令直接修改这些值。
最后,让我们看一下另一个键。
> TYPE ft:shakespeare/lady
ft_invidx
敏锐的读者可能会注意到“lady”这个词只在全文本字段 (speaker) 中被索引。存储在 ft_invidx 键中的数据是文本索引。
现在我们已经了解了 RediSearch 如何存储我们的数据,我们可以开始将更多实质性信息加载到数据库中并探索查询,但这将不得不等到几周后发布的 RediSearch 精通指南第二部分。