在redis数据库的值中搜索

pgvzfuti  于 2023-08-02  发布在  Redis
关注(0)|答案(4)|浏览(194)

我是Redis DB的新手。在阅读了一些文档,查看了互联网上的一些示例,并浏览了www.example.com之后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

应该如何以正确的方式去做呢?我也可以想象id将在ZSET中,需要搜索的数据可能是分数,但这使得只能使用范围而不是seraches。
也提前感谢你,问候,Tamas

答案摘要:实际上,两个答案都表明Redis的设计并不是用来搜索键值的。如果这个用例是必要的,那么需要实现变通方法,如我的原始解决方案或下面的解决方案所示。

Eli下面的解决方案比我原来的方案有一个更好的性能,因为对键的访问可以被认为是恒定的,只有id列表需要迭代,对于访问,这将给予O(const)运行时间。该数据模型还允许一个人可能与其他人具有相同的电话号码,以此类推,也用于姓名等。所以1-n关系也是可能(我会用旧ERD术语说)。
这种解决方案的缺点是,它比我的占用空间大得多,并且电话号码的起始数字只知道,无法搜索
感谢两个答案。

htrmnn0y

htrmnn0y1#

Redis适用于需要以非常高的频率访问和更新数据的用例,并且您可以从数据结构(哈希,集合,列表,字符串或排序集合)的使用中受益。它是为了满足非常具体的用例而设计的。如果你有一个通用的用例,比如非常灵活的搜索,你会更好地为这个目的而构建的东西,比如ElasticSearch或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:”)创建一个新键。每个key都指向一组id,其中包含您想要的用户的所有信息。例如,当您搜索电话时,您将执行以下操作:

HGETALL 'phone:123-456-7891'


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

for id in results:
    HGET id 'name'


这里的成本是O(m),其中m是给定电话号码的用户数,这在Redis上是一个非常快的操作,因为它是如何优化速度的。在您的情况下,这将是矫枉过正,因为您可能不需要事情进行得如此之快,并且您更喜欢灵活的搜索,但这就是您如何做到这一点。

kzmpq1sx

kzmpq1sx2#

redis很棒,但它不是为搜索键以外的任何东西而构建的。如果不构建额外的数据集来存储项以方便查询,您就无法查询值,但即使这样,您也无法获得真正的搜索,只是更多的维护,内存的低效使用,yada,yada...
这个问题已经解决了,你有一些阅读要做:-D

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

How do I search strings in redis?

为什么在搜索文档时使用MongoDB而不是redis是明智的... What's the most efficient document-oriented database engine to store thousands of medium sized documents?

s4n0splo

s4n0splo3#

Redis原始二级指数

这里公认的答案是正确的,因为在Redis中处理搜索的传统方式是通过围绕集合和排序集合构建的secondary indices
例如,在

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 Source Available许可证下可用
有一个新的模块可以插入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]"


要返回与模式匹配的所有项,请参阅query syntax以了解更多详细信息

qij5mzcb

qij5mzcb4#

zeeSQL是一个新颖的Redis模块,具有SQL和二级索引功能,允许通过Redis键的值进行搜索。
您可以将其设置为跟踪所有散列的值并将它们放入标准SQL表中。
对于通过电话号码和姓名搜索人员的示例,您可以执行以下操作。

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

字符串
此时,zeeSQL将跟踪所有以customer开头的散列,并将它们放入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

相关问题