【问题标题】:Composite indexing using Redis in a hierarchical data model在分层数据模型中使用 Redis 进行复合索引
【发布时间】:2023-03-20 23:42:01
【问题描述】:

我有一个这样的数据模型:
字段:

  1. 柜台号码(例如 00888、00777、00123 等)
  2. 计数器代码(例如 XA、XD、ZA、SI 等)
  3. 开始日期(例如 2017-12-31 ...)
  4. 结束日期(例如 2017-12-31 ...)
  5. 其他柜台日期(例如 xxxxx)

当前的数据结构组织是这样的(根和多子格式):

counter_num + counter_code
       ---> start_date + end_date --> xxxxxxxx
       ---> start_date + end_date --> xxxxxxxx
       ---> start_date + end_date --> xxxxxxxx

例子:

00888 + XA
       ---> Jan 10 + Jan 20 --> xxxxxxxx
       ---> Jan 21 + Jan 31 --> xxxxxxxx
       ---> Feb 01 + Dec 31 --> xxxxxxxx

00888 + ZI
       ---> Jan 09 + Feb 24 --> xxxxxxxx
       ---> Feb 25 + Dec 31 --> xxxxxxxx

00777 + XA
       ---> Jan 09 + Feb 24 --> xxxxxxxx
       ---> Feb 25 + Dec 31 --> xxxxxxxx

今天检索以两种方式发生:

//Fetch unique counter data using all the composite keys
counter_number + counter_code + date (start_date <= date <= end_date)

//Fetch all the counter codes and corresponding data matching the below conditions
counter_number + date (start_date <= date <= end_date)

在 redis 中对此进行建模的最佳方法是什么,因为我需要缓存一些经常命中的数据。我觉得排序集应该以某种方式做到这一点,但无法对其建模。

更新:

只是为了消除混淆,这里的询问不是针对 SQL“BETWEEN”之类的查询。 '因为我不知道 start_date 和 end_date 值是什么。认为它们只是列名。

我不想要的是

SELECT * FROM redis_db  
WHERE counter_num AND 
date_value BETWEEN start_date AND end_date

我想要的是

SELECT * FROM redis_db
WHERE counter_num AND
start_date <= specifc_date AND end_date >= specific_date

注意:要求非常接近 Redis 多维索引文档中提出的二维索引

https://redis.io/topics/indexes#multi-dimensional-indexes

我理解了这个概念,但无法消化给出的实现细节。

【问题讨论】:

  • 澄清一下,您是否尝试在日期字段中使用动态查询,例如“获取位于 DATE1 和 DATE2 之间的 00888 + XA 的所有值”,其中日期可能会有所不同?
  • 没有。请阅读我对您的回答的评论

标签: redis data-modeling


【解决方案1】:

由于您的问题对于您希望如何查询数据仍然有些模糊,因此尚不清楚如何解决您的问题。然而,考虑到这一点,以下是我对如何为您的数据建模的想法:

更新答案,详细说明如何使用 SORTED SET

我已编辑此答案,以便能够以您可以按动态日期范围查询的方式存储您的值。此编辑假定您的数据库值是时间戳,因为该值是一次,而不是 2,如您当前的设置。

是的,您是正确的,使用排序集将能够完成此操作。我建议您始终为这些排序集中的分数组件使用 Unix 时间戳值。

如果您还不熟悉 redis,让我们解释一下索引限制。 Redis 是一个简单的键值对,旨在通过键快速检索值。由于这种设计,它不包含传统 DBMS 的许多功能,例如索引列。

在redis中,你通过一个key来完成索引,嵌套最多的key-like结构有HASH和SORTED SET,但是你只能得到2个key-like结构。在 HASH 中,您有一个键(与任何数据类型相同)和一个内部哈希键,它可以采用任何字符串的形式。

在 SORTED SET 中,您有键(与任何数据类型相同)和一个数值。

HASH 非常适合用来组织分组数据。

如果您想按一系列值进行查询,SORTED SET 是很好的选择。这可能非常适合您的数据。

您的SORTED SET 如下所示:

key
00888:XA =>
            score       (date value)   value
            1452427200  (2016-01-10)   xxxxxxxx
            1452859200  (2016-01-10)   yyyyxxxx
            1453291200  (2016-01-10)   zzzzxxxx

让我们用一个更直观的例子,2017 年尤文图斯大名单:

要生成下表中的 SORTED SET,请在您的 redis 客户端中发出以下命令:

ZADD JUVENTUS 32 "Emil Audero" 1 "Gianluigi Buffon" 42 "Mattia Del Favero" 36 "Leonardo Loria" 25 "Neto" 15 "Andrea Barzagli" 4 "Medhi Benatia" 19 "Leonardo Bonucci" 3 "Giorgio Chiellini" 40 "Luca Coccolo" 29 "Paolo De Ceglie" 26 "Stephan Lichtsteiner" 12 "Alex Sandro" 24 "Daniele Rugani" 43 "Alessandro Semprini" 23 "Dani Alves" 22 "Kwadwo Asamoah" 7 "Juan Cuadrado" 6 "Sami Khedira" 18 "Mario Lemina" 46 "Mehdi Leris" 38 "Rolando Mandragora" 8 "Claudio Marchisio" 14 "Federico Mattiello" 45 "Simone Muratore" 20 "Marko Pjaca" 5 "Miralem Pjanic" 28 "Tomás Rincón" 27 "Stefano Sturaro" 21 "Paulo Dybala" 9 "Gonzalo Higuaín" 34 "Moise Kean" 17 "Mario Mandzukic"


 Jersey  Name                     Jersey  Name 
 32      Emil Audero              23     Dani Alves 
 1       Gianluigi Buffon         42      Mattia Del Favero 
 36      Leonardo Loria           25      Neto 
 15      Andrea Barzagli          4       Medhi Benatia 
 19      Leonardo Bonucci         3       Giorgio Chiellini 
 40      Luca Coccolo             29      Paolo De Ceglie 
 26      Stephan Lichtsteiner     12      Alex Sandro 
 24      Daniele Rugani           43      Alessandro Semprini 
 22     Kwadwo Asamoah            7     Juan Cuadrado 
 6     Sami Khedira               18     Mario Lemina 
 46     Mehdi Leris               38     Rolando Mandragora 
 8     Claudio Marchisio          14     Federico Mattiello 
 45     Simone Muratore           20     Marko Pjaca 
 5     Miralem Pjanic             28     Tomás Rincón 
 27     Stefano Sturaro           21     Paulo Dybala 
 9     Gonzalo Higuaín            34     Moise Kean 
 17     Mario Mandzukic 

通过一系列球衣号码查询名册:

ZRANGEBYSCORE JUVENTUS 1 5
Output: 
1) "Gianluigi Buffon"
2) "Giorgio Chiellini"
3) "Medhi Benatia"
4) "Miralem Pjanic"

请注意,不会返回分数,但是ZRANGEBYSCORE 命令按分数按 ASC 顺序排列结果。 要添加分数,请将“WITHSCORES”附加到命令中,如下所示:ZRANGEBYSCORE JUVENTUS 1 5 WITHSCORES

通过使用 ZRANGEBYSCORE,您应该能够查询具有日期范围的任何键(计数器编号 + 计数器代码), 产生该范围内的值。



原文:以下是我的原答案,推荐HASH

根据您的示例,我建议您使用HASH

使用散列,您将有一个主键来查找散列(例如00888:XA)。然后在哈希中,您有键 -> 值对(例如 2017-01-10:2017-01-20 -> xxxxxxxx)。我更喜欢使用冒号字符 : 来分隔或标记我的密钥组件,但您可以使用任何分隔符。

HASH 很好地遵循了您的示例数据结构:

key
00888:XA =>
            hashkey                  value
            2017-01-10:2017-01-20    xxxxxxxx
            2017-01-21:2017-01-31    yyyyxxxx
            2016-02-01:2016-12-31    zzzzxxxx

key
00888:ZI =>
            hashkey                  value
            2017-01-10:2017-01-20    xxxxxxxx
            2017-01-21:2017-01-31    xxxxyyyy
            2016-02-01:2016-12-31    xxxxzzzz

查询数据时,您可以使用HGET key hashkey 查询,而不是GET key。设置值也一样,而不是SET key value,使用HSET key hashkey value

示例命令

HSET  00777:XA  2017-01-10:2017-01-20  xxxxxxxx
HSET  00777:XA  2017-01-21:2017-01-31  yyyyyyyy
HSET  00777:XA  2016-02-01:2016-12-31  zzzzzzzz

(注意:还有一个HMSET 将其简化为单个命令) 那么:

HGET  00777:XA  2017-01-21:2017-01-31

将返回yyyyyyyy

除非对您的数据有一些特定的性能考虑或其他目标,否则我认为哈希对您的系统非常有用。

如果您想获取给定哈希的所有哈希键或所有值,使用 HKEYSHVALSHGETALL 等命令也非常方便。

【讨论】:

  • 这里的主要问题是,我不是在寻找类似 SQL 的“BETWEEN”查询。 '因为我不知道开始日期和结束日期是什么。我所知道的是当前日期或某个特定日期,我想提取所有记录“WHERE start_date = specific_date”。请记住 specific_date 是我所拥有的,而 start_date 和 end_date 只是列名。
  • 你将在这个实现中使用什么堆栈?例如,在该页面上提到了一个 Ruby 库 [redimension](github.com/antirez/redimension),它抽象出使用二进制编码交错值的多重索引的实现细节,
  • 是的。但我打算使用 C。想知道为什么 Salvatore 在 ruby​​ 中实现了它,而不是在 C 中。
【解决方案2】:

我不太可能及时完成这项工作以获得赏金,但到底是什么......

这听起来像是地理散列的工作。 Geohashing 是您想要索引二维(或更高)数据集时所做的事情。例如,如果您有一个城市数据库,并且希望能够快速响应“查找 X 50 公里范围内的所有城市”之类的查询,则可以使用地理哈希。

对于这个问题,您可以将start_dateend_date 视为xy 坐标。通常在地理散列中,您在数据集中搜索空间中特定点附近或空间的某个有界区域中的点。在这种情况下,您只有一个坐标的下限和另一个坐标的上限。但我想实际上整个数据集都是有界的,所以这不是问题。

如果 Redis 中有一个库可以做到这一点,那就太好了。可能有,如果你看起来足够努力的话。较新版本的 Redis 具有内置的地理哈希功能。查看以GEO 开头的命令。但它并没有声称非常准确,而且它是为球体表面设计的,而不是平面。

据我所知,您有 3 个选项:

  • 将您的搜索空间映射到球体的一小部分,最好靠近赤道。使用 Redis GEO 命令。要搜索,请在覆盖您尝试搜索的三角形的圆圈上使用GEOSPHERE,考虑到内置的不准确性和通过映射到球体上得到的失真,然后过滤结果以获得实际在三角形。
  • 为 Redis 找到一些适用于平面空间且比 GEO 更准确的第 3 方地理哈希客户端。
  • 阅读此答案的其余部分,或其他有关 geohashing 的入门知识,然后在 Redis 之上自行实施。这是最难(但最具教育意义)的选择。

如果您有一个使用数字排序索引数据的数据库,这样您就可以执行诸如“查找z 位于ab 之间的所有行/记录”之类的查询,您可以构建一个geohash 索引在它之上。假设坐标是(非负)整数xy。然后添加一个整数值列z,并按z 索引。要计算z,请将xy 写入二进制,然后从每个中取交替数字。示例:

x =     969 = 0 1 1 1 1 0 0 1 0 0 1 
y =    1130 =  1 0 0 0 1 1 0 1 0 1 0
z = 1750214 = 0110101011010011000110

请注意,例如,该索引允许您查找位于01011000000000000000000101101111111111111111 之间的所有以z 定位的记录。换句话说,所有z010110 开头的记录。或者换句话说,您可以找到所有x001 开头和y110 开头的记录。这组记录对应于我们试图搜索的二维空间中的一个正方形。

不是所有的方块都可以用这种方式搜索。我们称这些为可搜索方块。假设客户端发送一个请求,请求所有记录的(x,y) 位于特定矩形内。 (或者一个圆形,或者一些其他合理的几何形状。)然后你需要找到一组覆盖矩形的可搜索正方形。然后,对于您选择的每个方格,查询数据库中该方格内的记录并将结果发送给客户端。 (但您必须过滤结果,因为并非正方形中的所有记录实际上都在原始矩形中。)

需要达到一个平衡点。如果您选择少量大型特殊方格,您最终可能会覆盖比您需要的更大的地图区域;对数据库的查询将返回许多您必须过滤掉的额外结果。或者,如果您使用大量特殊的小方块,您将对数据库进行大量查询,其中很多都不会返回任何结果。

我在上面说过xy 可以是start_timeend_time。但实际上,您的数据集的分布不会像大多数地理散列的使用那样对称。因此,如果您使用x = end_time + start_timey = end_time - start_time,性能可能会更好(或更差)。

【讨论】:

  • 赏金还剩不到 5 分钟,所以我奖励你。到目前为止,如果不是直接代码 sn-p,这是最可接受的答案。我将尝试实施选项 3 并检查选项 1。但是将来如果您找到一种最简单的方法或一个涵盖 nd 范围索引或至少 2-d 索引的良好 C sn-p,请不要忘记在这个空间发表评论。谢谢
  • 谢谢!没时间找代码了,对C也不是很了解。另外,我认为您没有在问题本身中提到它在 C 中。 (您应该用您使用的语言标记问题。)
猜你喜欢
  • 2019-11-17
  • 2012-08-10
  • 1970-01-01
  • 2021-01-07
  • 2020-09-27
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 2017-02-13
相关资源
最近更新 更多