小编典典

如何使Redis缓存中的数据层次结构(树)的某些部分无效

redis

我有一些产品数据需要在Redis缓存中存储多个版本。数据由JSON序列化的对象组成。获取纯(基本)数据的过程非常昂贵,将其自定义为不同版本的过程也很昂贵,因此我想缓存所有版本以尽可能进行优化。数据结构如下所示:

                                    BaseProduct
                                         /\
                                      /      \
                                   /            \
                                /                  \
                             /                        \
           CustomisedProductA                          CustomisedProductB
                  /  \                                       /  \
CustomisedProductA1  CustomisedProductA2   CustomisedProductB1  CustomisedProductB2

这里的总体思路是:

  • 数据库中存储有基本产品。
  • 可以将一种定制级别应用于该产品-例如,有关该产品针对销售地区的特定版本的信息。
  • 可以在其中应用第二级定制-例如,有关某个区域内特定商店的此产品的信息。

数据以这种方式存储,因为数据检索/计算过程的每个步骤都很昂贵。第一次为某个区域检索特定产品时,将执行一组定制以使其成为特定于区域的产品。第一次为商店检索特定产品时,我需要根据区域产品执行定制以生成特定于商店的产品。

之所以出现此问题,是因为我可能需要通过以下几种方法来使数据无效:

  • 如果基本产品数据发生更改,则整个树需要失效,并且所有内容都需要重新生成。我可以通过将整个结构存储在散列中并通过其键删除散列来实现此目的。
  • 如果产品的第一批定制更改(即中间级别),那么我也需要使该级别下的节点无效。例如,如果CustomizedProductA的定制受到更改的影响,则需要使CustomizedProductA,CustomizedProductA1和CustomizedProductA2过期。
  • 如果产品的第二组自定义更改(即最低级别),则该节点需要无效。我可以通过调用HDEL key field(例如HDEL product CustomisedProductA:CustomisedProductA1)在哈希中实现此目的。

因此,我的问题是:有没有一种方法可以表示这种类型的多级数据结构,以便在仅使部分树无效的情况下实现将数据存储在多级中的性能?或者,我是否仅限于使整个树(DEL key)或特定节点(HDEL key field)失效,而两者之间什么也没有?


阅读 956

收藏
2020-06-20

共1个答案

小编典典

至少有3种不同的方式,每种方式各有利弊。

第一种方法是对树使用非原子性的临时扫描,以识别和使树的第二层(第一组自定义)无效(删除)。为此,请为您的Hash字段使用层级命名方案,然后使用遍历它们HSCAN。例如,假设哈希的键名是产品的ID(例如ProductA),则将第一个自定义版本的第一个版本的字段名使用“
0001:0001”,将其第二个版本的字段使用“
0001:0002”,等等。同样,‘0002:0001’将是第二个自定义的第一个版本,以此类推…然后,确实找到所有自定义42的版本,使用HSCANProductA 0 MATCH0042:*HDEL回复中的字段,并重复进行直到光标为零。

相反的方法是主动“索引”每个定制的版本,以便您可以高效地获取它们,而不用执行哈希的完整扫描。解决方法是使用Redis的集合-
您保留一个集合,其中包含给定产品版本的所有字段名称。版本可以是顺序的(例如在我的示例中),也可以是其他任何版本,只要它们是唯一的即可。维护这些索引的代价是-
每次添加或删除产品的自定义和/或版本时,都需要保持与这些集合的一致性。例如,创建版本将类似于:

HSET ProductA 0001:0001 "<customization 1 version 1 JSON payload"
SADD ProductA:0001 0001

请注意,这两个操作应该在单个事务中(即使用MULTI\EXEC块或EVALLua脚本)。进行此设置后,使自定义设置无效仅是调用SMEMBERS相关的Set并从Hash(以及Set本身)中删除其中的版本。不过,请务必注意,从大型Set读取所有成员可能很耗时-1K成员并没有那么糟糕,但是对于大型Set来说,则是SSCAN

最后,您可以考虑使用排序集而不是哈希。尽管在此用例中可能不太直观,但“排序集”将使您执行所需的所有操作。但是,使用它的代价是与Hash的O(1)相比,O(logN)用于添加/删除/读取的复杂性增加了,但是给定的数字并不明显。

要释放排序集的功能,您将使用字典顺序,因此所有排序集的成员都应具有相同的分数(例如,使用0)。就像哈希一样,每种产品都将以有序集表示。Set的成员与Hash字段的等效项,即自定义版本。“技巧”以一种允许您执行范围搜索(或如果可能的话,则为2级无效)的方式构造成员。这是一个看起来像的示例(请注意,这里的键ProductA不是哈希,而是排序集):

ZADD ProductA 0 0001:0001:<JSON>

要读取自定义版本,请使用ZRANGEBYLEX ProductA [0001:0001: [0001:0001:\xff答复中的JSON并将其拆分,并使用删除整个自定义项ZREMRANGEBYLEX

2020-06-20