在分层数据模型中使用Redis进行复合索引

时间:2017-05-24 16:40:53

标签: redis data-modeling

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

  1. 计数器编号(例如00888,00777,00123等)
  2. 计数器代码(例如XA,XD,ZA,SI等)
  3. 开始日期(例如2017-12-31 ......)
  4. 结束日期(例如2017-12-31 ......)
  5. 其他反日期(例如xxxxx)
  6. 当前的数据结构组织是这样的(根和多子格式):

    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&#34; BETWEEN&#34;喜欢查询。 &#39;因为我不知道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

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

2 个答案:

答案 0 :(得分:3)

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

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

我编辑了这个答案,以便能够以动态日期范围查询的方式存储您的值。此编辑假定您的数据库值是时间戳,因为值是一次,而不是2,就像在当前设置中一样。

是的,你是正确的,使用排序集将能够实现这一点。我建议你总是在这些有序集合中为得分组件使用Unix时间戳值。

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

在redis中,您可以使用键完成索引,并且HASH和SORTED SET中可以使用嵌套最多的类似键的结构,但是您只能获得2个类似键的结构。在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顺序排序结果。 要添加分数,请附加&#34; WITHSCORES&#34;命令如下:ZRANGEBYSCORE JUVENTUS 1 5 WITHSCORES

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

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

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

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

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

在查询数据时,您将使用GET key进行查询,而不是HGET key hashkey。设置值相同,而不是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

除非您的数据有某些特定的性能考虑因素或其他目标,否则我认为Hashes将非常适合您的系统。

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

答案 1 :(得分:3)

我不太可能及时为赏金做到这一点,但到底是什么......

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

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

如果在Redis中有一个用于执行此操作的库,那将会很好。如果你足够努力,可能会有。较新版本的Redis具有内置的geohashing功能。请参阅以GEO开头的命令。但它并没有声称是非常准确的,它是专为球体表面而不是平面设计的。

据我所知,你有3个选择:

  • 将搜索空间映射到球体的一小部分,最好是在赤道附近。使用Redis GEO命令。要搜索,请在覆盖您要搜索的三角形的圆上使用GEOSPHERE,同时考虑内置的不准确性以及通过映射到球体上获得的失真,然后过滤结果以获得实际的结果在三角形里面。
  • 为Redis找一些第三方geohashing客户端,它在平面空间工作,比GEO更准确。
  • 阅读本答的其余部分或其他关于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

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

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

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

我在上面说xy可能是start_timeend_time。但实际上,数据集的分布与大多数geohashing的使用不一样。因此,如果您使用x = end_time + start_timey = end_time - start_time,性能可能会更好(或更糟)。