【问题标题】:Redis and Querying ValuesRedis 和查询值
【发布时间】:2012-07-02 00:11:24
【问题描述】:

Redis 在概念上与我使用的传统 SQL 数据库不同,我正在尝试确定它是否适合我的项目...我一直在环顾四周,但似乎找不到我的答案问题。

我需要存储一组用户,每个用户都有一个唯一的 ID 和与其关联的多个值(例如他们的名称)。看来我可以简单地将它们存储为哈希:

user:fef982dcfe1a7bcba4849b4c281bba95
"username" "andrewm" "name" "Andrew"

我还有一堆要存储的消息,每个消息都有一些属性,例如发件人和收件人:

message:1a7bcba4849b4c281bfef98a952dcfeb
"sender" "fef982dcfe1a7bcba4849b4c281bba95" "recipient" "82dcfe1a7bcba4849b4c281bba95fef9" "message" "Hi!"

我的问题是,我将如何检索由特定用户(由他们的哈希指定)发送的所有消息。我应该改用传统的关系数据库,还是像 MongoDB 这样的 NoSQL 数据库(我以前用过)?如果是这样,有人对高性能商店有什么建议吗?我不会进行任何真正的搜索(即 MySQL LIKE 查询)——真的只是键值查找。

【问题讨论】:

    标签: redis


    【解决方案1】:

    当然可以使用 Redis 对这些数据进行建模,但您需要考虑数据结构和访问路径。使用 Redis,访问路径不是隐式管理的(就像 RDBMS/MongoDB 中的索引一样)。

    对于提供的示例,您可以:

    user:<user hash> -> hash of user properties
    user:<user hash>:sent -> set of <msg hash>
    user:<user hash>:received -> set of <msg hash>
    message:<msg hash> -> hash of message properties
    

    添加/删除消息意味着在添加/删除消息对象本身的基础上维护与发送者和接收者相对应的 *:sent 和 *:received 集。

    检索给定用户发送或接收的消息只是一个 SMEMBERS 命令,如果您还想同时检索消息的属性,则需要一个 SORT:

    # Get a list of message hash codes only in one roundtrip
    smembers user:<user hash>:received
    
    # Get a list of message contents in one roundtrip
    sort user:<user hash>:received by nosort get message:*->sender get message:*->message
    

    有关使用排序的基本原理,请参阅:

    注意 1:对于 Redis,最好使用整数作为键而不是 UUID 或哈希码(尤其是在集合中),因为它们以更有效的方式存储。

    注意2:如果您需要对消息进行排序,则必须使用列表而不是集合。结果是只能删除最旧的消息,并且只能以有效的方式添加新消息。您可能还需要为所有消息添加一个全局列表。

    【讨论】:

    • 非常感谢您的解释——这很有帮助。我会进一步看一下:)
    • 您好,谢谢,非常有帮助。如果“添加/删除消息意味着维护 *:sent 和 *:received 集”,是否需要更多空间来存储数据,从而占用更多内存空间?如果经常做这种“多店”,会不会是性能问题?感谢您的帮助
    • 是的,它表示要存储更多数据。关于性能,您应该使用流水线来分摊对给定操作执行多个命令的影响。见redis.io/topics/pipelining
    【解决方案2】:

    Redis的基本数据类型不支持多条件查询、全文搜索等,因此我们对Redis源码进行了修改,通过辅助索引将Redis转化为可以像SQL数据一样使用的数据库。

    这个项目主页是https://oncedb.com

    OnceDB 不会改变 Redis 的数据存储结构。 Redis数据库文件可以直接在OnceDB中操作,然后返回Redis使用。

    索引搜索

    创建索引

    全文搜索的性能很差。您可以通过创建索引来提高性能。方法是为索引字段创建有序列表,然后在条件查询时对这些有序列表进行交集查询操作。

    # Create hash data
    hmset article:001 poster dota visit 21 key js
    hmset article:002 poster dota visit 11 key c
    hmset article:003 poster like visit 34 key js
    hmset article:004 poster like visit 44 key c
    

    然后我们为上述字段创建索引,权重分数设置为:202000201,一个关于时间的整数,值为文章的ID值

    # Create indexed
    zadd *article.poster:dota 20200201 001 20200201 002
    zadd *article.poster:like 20200201 003 20200201 004
    zadd *article.key:js 20200201 001 20200201 003
    zadd *article.key:c 20200201 002 20200201 004
    # "visit" using its value as the weight score
    zadd *article.visit 21 001 11 002 34 003 44 004
    

    按索引查询

    找到*article.key:js和*article.poster:dota这两个索引的交集,并将它们存储在*tmp1有序列表中:

    zinterstore *tmp1 2 *article.key:js *article.poster:dota
    > 1
    

    然后*tmp1存储满足key=js和poster=dota条件的ID集合:

    zrange *tmp1 0 -1
    > 001
    

    可以使用zrangehmget命令打印对应的HASH值:

    zrangehmget *tmp1 0 -1 article: key poster
    1) 001
    2) 40400402
    3) js
    4) dota
    5) 
    6) 
    

    结果与直接全文搜索key = js 和poster = dota 一样

    hsearch article:* key = js poster = dota
    1) article:001
    2) js
    3) dota
    

    搜索范围

    比如要搜索访问次数在20到30之间的数据,key=js,可以通过控制权重来实现

    创建一个临时索引,只取*article.visit的权重和key=js的数据

    zinterstore *tmp2 2 *article.key:js *article.visit weights 0 1
    > 2
    

    获取 20 到 30 之间的数据

    zrangebyscore *tmp2 20 30
    > 001
    

    可以使用zrangehmgetbyscore打印对应的hash数据:

    zrangehmgetbyscore *tmp2 20 30 article: key visit
    1) 001
    2) 21
    3) js
    4) 21
    5) 
    6) 
    

    结果与使用全文搜索的结果一致:

    hsearch article:* visit >= 20 visit <= 30 key = js
    1) article:001
    2) 21
    3) 
    4) js
    

    因为有两个相同的字段,visit> = 20 visit

    更多OnceDB扩展指令可查看:Search, query, calculate, and sum instructions in OnceDB

    自动索引

    Redis 索引的创建和维护不是很方便。 OnceDB可以选择在数据修改时自动创建辅助索引。

    创建索引:更新模式字段操作符值...

    使用 upsert / insert / update 指令和特殊运算符自动创建索引:

    上面的例子可以写成:

    upsert article id @ 001 poster ? dota visit / 21 key ? js
    upsert article id @ 002 poster ? dota visit / 11 key ? c
    upsert article id @ 003 poster ? like visit / 34 key ? js
    upsert article id @ 004 poster ? like visit / 44 key ? c
    

    操作员:

    @: 主键 ?:组索引 /: 排序索引

    操作后自动创建索引: *article *article.poster:dota *article.poster:like *article.visit *article.key:js *article.key:c

    多条件索引查询:查找schema from to field operator value ...

    对于有索引的字段,可以使用 find 命令通过索引字段进行查询。例如查询:key = js 和poster = dota 的数据。您可以使用 ”?”表示这两个字段是分组索引:

    find article 0 -1 key ? js poster ? dota
    1) 1
    2) article:001
    3) js
    4) dota
    

    1代表数据总量。如果为-1,则表示使用全文搜索,性能较差。

    索引范围查询

    您可以添加@指定索引范围,并使用+指定分数权重范围使用哪个索引字段。

    find article 0@20 -1@30 key ? js visit /+ *
    1) 1
    2) article:001
    3) js
    4) 21
    

    删除索引

    OnceDB 不存储索引定义。删除时需要手动指明哪些字段包含索引。您需要指定字段名称和索引运算符。

    remove article @ 001 key ? poster ? visit /
    

    您还可以自定义索引名称和权重分数。更多说明请见:OnceDB data modification and query help documentation

    【讨论】:

      猜你喜欢
      • 2014-11-18
      • 2011-07-20
      • 2022-01-04
      • 2016-08-28
      • 2011-03-25
      • 1970-01-01
      • 1970-01-01
      • 2018-09-27
      • 1970-01-01
      相关资源
      最近更新 更多