24

我是使用 Redis DB 的新手。在阅读了一些文档并查看了 Internet 上的一些示例并扫描了 stackoverflow.com 之后,我可以看到 Redis 非常快,可扩展性很好,但这要付出代价,我们必须考虑我们的数据将如何在设计时访问以及他们必须进行哪些操作。我可以理解这一点,但我对使用普通的旧 SQL 在数据中搜索如此简单但缓慢的内容感到有些困惑。我可以使用 KEY 命令以一种方式执行此操作,但它是 O(N) 操作而不是 O(log(N))。所以我会失去Redis的优势之一。

更有经验的同事在这里说什么?

让我们举个例子:我们需要存储个人数据大约。100.000 人,这些数据需要按姓名、电话号码进行搜索。

为此,我将使用以下结构:

1. SET for storing all persons' ids {id1, id2, ...} 
2. HASH for each person to store personal data and name it 
like map:<id> e.g. map:id1{name:<name>, phone:<number>, etc...}

解决方案1:

1. HASH for storing all persons' ids but the key should be the phone number
2. Then with the command KEY 123* all ids could be retrieved who have a phone number 
sarting with 123. On basis of the ids also the other personal data could be retrieved.
3. So forth for each data to be searched for a separate HASH should be created.

但是这个解决方案的一个主要缺点是属性值也必须是唯一的,这样电话号码和 HASH 中的 id 的分配就不会产生歧义。另一方面,O(N)运行时间并不理想。

此外,这会使用比必要更多的空间,并且KEY 命令会降低访问性能。( http://redis.io/commands/keys )

应该如何以正确的方式完成?我也可以想象 ids 会进入 ZSET 并且需要搜索的数据可能是分数,但这使得只能使用范围而不是 seraches。

也提前谢谢你,问候, Tamas

答案摘要: 实际上,两个响应都表明 Redis 并非旨在搜索键的值。如果此用例是必要的,则需要按照我的原始解决方案或以下解决方案中所示实施变通方法。

Eli的以下解决方案比我原来的解决方案具有更好的性能,因为对密钥的访问可以被认为是恒定的,只需要迭代 id 列表,因为访问这将提供O(const)运行时间。该数据模型还允许一个人可能与其他人拥有相同的电话号码,等等也可以用于姓名等......所以1-n关系也是可能的(我会说旧的ERD术语)。

该解决方案的缺点是,它比我的占用更多空间,并且无法搜索仅知道起始数字的电话号码。

感谢您的回复。

4

4 回答 4

28

Redis 适用于需要以非常高的频率访问和更新数据以及从使用数据结构(散列、集合、列表、字符串或排序集)中受益的用例。它是为满足非常具体的用例而设计的。如果您有一个非常灵活的搜索之类的一般用例,那么为此目的而构建的东西(例如弹性搜索或 SOLR)会更好地为您服务。

也就是说,如果您必须在 Redis 中执行此操作,我会这样做(假设用户可以共享姓名和电话号码):

name:some_name -> set([id1, id2, etc...])
name:some_other_name -> set([id3, id4, etc...])

phone:some_phone -> set([id1, id3, etc...])
phone:some_other_phone -> set([id2, id4, etc...])

id1 -> {'name' : 'bob', 'phone' : '123-456-7891', etc...}
id2 -> {'name' : 'alice', 'phone' : '987-456-7891', etc...}

在这种情况下,我们为每个姓名(前缀为“name:”)和每个电话号码(前缀为“phone:”)创建一个新密钥。每个键都指向一组 id,其中包含您想要为用户提供的所有信息。例如,当您搜索电话时,您将执行以下操作:

HGETALL 'phone:123-456-7891'

然后循环遍历结果并以您选择的语言返回每个(在我们的示例中为名称)的任何信息(您可以在 Redis 机器上的服务器端 Lua 中完成这整件事,以更快并避免网络往返 -第四,如果你愿意):

for id in results:
    HGET id 'name'

您在这里的成本将是O(m)具有m给定电话号码的用户数量,这将是 Redis 上的一个非常快速的操作,因为它针对速度进行了优化。在您的情况下,这将是矫枉过正,因为您可能不需要事情走得这么快,而且您更喜欢灵活的搜索,但这就是您的做法。

于 2013-06-19T21:04:04.543 回答
9

redis 很棒,但它不是为搜索键以外的任何东西而构建的。如果不构建额外的数据集来存储项目以促进此类查询,您根本无法查询值,但即使那样您也无法获得真正的搜索,只是更多的维护,内存使用效率低下,yada,yada ...

这个问题已经解决了,你有一些阅读要做:-D

要搜索字符串,在 redis 和其他很酷的东西中构建自动完成功能......
如何在 redis 中搜索字符串?

为什么在文档内部搜索时使用 MongoDB 而不是 redis 是明智的…… 存储数千个中型文档的最有效的面向文档的数据库引擎是什么?

于 2013-06-19T18:45:05.633 回答
2

zeeSQL是具有 SQL 和二级索引功能的新型 Redis 模块,允许按 Redis 键的值进行搜索。

您可以将其设置为跟踪所有哈希值并将它们放入标准 SQL 表中。

对于您通过电话号码和姓名搜索人员的示例,您可以执行类似的操作。

> ZEESQL.CREATE_DB DB
"OK"
> ZEESQL.INDEX DB NEW PREFIX customer:* TABLE customer SCHEMA id INT name STRING phone STRING

此时zeeSQL将跟踪所有以开头的哈希custumer并将它们放入 SQL 表中。它将字段存储id为整数、name字符串和phone字符串。

您可以简单地将哈希添加到 Redis 来填充表,zeeSQL并使所有内容保持同步。

> HMSET customer:1 id 1 name joseph phone 123-345-2345
> HMSET customer:2 id 2 name lukas phone 234-987-4453
> HMSET customer:3 id 3 name mary phone 678-443-2341 

此时您可以查看客户表,您将找到您正在寻找的结果。

> ZEESQL.EXEC DB COMMAND "select * from customer"
1) 1) RESULT
2) 1) id
2) 2) name
2) 3) phone
3) 1) INT
3) 2) STRING
3) 3) STRING
4) 1) 1
4) 2) joseph
4) 3) 123-345-2345
5) 1) 2
5) 2) lukas
5) 3) 234-987-4453
6) 1) 3
6) 2) mary
6) 3) 678-443-2341

结果首先指定列的名称,然后指定列的类型,最后指定实际结果集。

zeeSQL 基于 SQLite,它支持所有用于过滤和聚合的 SQLite 语法。

例如,您可以搜索只知道电话号码前缀的人。

> ZEESQL.EXEC DB COMMAND "select name from customer where phone like 678%"
1) 1) RESULT
2) 1) name
3) 1) STRING
4) 1) mary

您可以在教程中找到更多示例:https ://doc.zeesql.com/tutorial#using-secondary-indexes-or-search-by-values-in-redis

于 2021-02-20T08:59:23.780 回答
2

Redis 中的原始二级索引

这里公认的答案是正确的,因为在 Redis 中处理搜索的传统方式是通过围绕 Sets 和 Sorted Sets 构建的二级索引。

例如

HSET Person:1 firstName Bob lastName Marley age 32 phoneNum 8675309

您将维护二级索引,因此您必须致电

SADD Person:firstName:Bob Person:1
SADD Person:lastName:Marley Person:1
SADD Person:phoneNum:8675309 Person:1
ZADD Person:age 32 Person:1

这使您现在可以执行类似搜索的操作

例如

SELECT p.age
FROM People AS p
WHERE p.firstName = 'Bob' and p.lastName = 'Marley' and p.phoneNum = '8675309'

变成:

ids = SINTER Person:firstName:Bob Person:lastName:Marley Person:phoneNum:8675309

foreach id in ids:
   age = HGET id age
   print(age)

这种方法的主要挑战是,除了设置起来相对复杂(它真的迫使你考虑你的模型)之外,原子化维护变得极其困难,特别是在分片环境中(交叉分片键约束可能成为有问题)因此键和索引可能会分开,迫使您必须定期循环并重建索引。

使用 RediSearch 的较新二级索引

警告:这使用 RediSearch 一个 Redis 模块,在 Redis 源可用许可证下可用

有一个更新的模块可以插入 Redis 中,它可以为您完成所有这些工作,称为RediSearch这可以让您声明二级索引,然后在您插入时为您处理所有内容的索引。对于上面的示例,您只需要运行

FT.CREATE person-idx ON HASH PREFIX 1 Person: SCHEMA firstName TAG lastName TAG phoneNumber TEXT age NUMERIC SORTABLE

那将声明索引,然后您需要做的就是将东西插入Redis,例如

HSET Person:1 firstName Bob lastName Marley phoneNumber 8675309 age 32

然后你可以运行:

FT.SEARCH person-idx "@firstName:{Bob} @lastName:{Marley} @phoneNumber: 8675309 @age:[-inf 33]"

要返回与模式匹配的所有项目,请参阅查询语法以获取更多详细信息

于 2021-10-06T14:58:21.427 回答