如何使Redis缓存

时间:2016-04-20 12:10:02

标签: caching redis

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

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

这里的一般想法是:

  • 存储在数据库中的基础产品。
  • 可以对此产品应用一级自定义 - 例如有关销售区域的此产品的特定版本的信息。
  • 可以在其中应用第二级自定义 - 例如有关该产品的信息,请访问该地区的特定商店。

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

问题来自于我可能需要通过以下几种方式使数据无效:

  • 如果基础产品数据发生变化,则需要使整个树无效,并且需要重新生成所有内容。 I can achieve this by storing the whole structure in a hash and deleting the hash by its key
  • 如果产品的第一组自定义更改(即中间级别),那么我也需要使此级别下的节点无效。例如,如果CustomisedProductA的自定义项受更改影响,我需要使CustomisedProductA,CustomisedProductA1和CustomisedProductA2过期。
  • 如果产品的第二组自定义更改(即底层),则该节点需要无效。我可以通过调用HDEL key field(例如HDEL product CustomisedProductA:CustomisedProductA1)来实现此目的。

因此,我的问题是:有没有办法表示这种类型的多级数据结构,以便允许在多个级别存储数据的性能,同时只允许部分树的无效?或者,我是否仅限于使整个树(DEL key)或特定节点(HDEL key field)过期,但两者之间没有任何内容?

1 个答案:

答案 0 :(得分:2)

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

第一种方法是使用树的非原子ad-hoc扫描来识别和无效(删除)树的第二级(第一组自定义)。为此,请为Hash的字段使用hierarichal命名方案,并使用HSCAN进行迭代。例如,假设您的Hash的密钥名称是产品的ID(例如ProductA),您可以使用类似'0001:0001'的字符作为第一个自定义的第一个版本的字段名称,'0001:0002'用于其第二个版本和等等。类似地,'0002:0001'将是第二个自定义第一版等,然后,找到所有自定义42的版本,使用HSCAN ProductA 0 MATCH 0042:*HDEL回复中的字段,并重复直到光标为零。

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

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

请注意,这两个操作应该在一个事务中(即使用MULTI\EXEC块或EVAL Lua脚本)。设置完成后,自定义无效只需在相关Set上调用SMEMBERS并从Hash中删除其中的版本(以及Set本身)。然而,重要的是要注意,从大型集中读取所有成员可能非常耗时 - 1K成员并不那么糟糕,但对于较大的集合,则SSCAN

最后,您可以考虑使用排序集而不是哈希。虽然在这个用例中可能不太直观,但Sorted Set将允许您执行所需的所有操作。然而,使用它的价格是与Hash的O(1)相比,添加/删除/读取的O(logN)的复杂性增加,但是给出数字差异并不显着。

要释放排序集的权力,您将使用词典排序,因此所有排序集的成员应具有相同的分数(例如,使用0)。每个产品都将由一个排序集表示,就像哈希一样。 Set的成员是Hash字段的等价物,即自定义版本。 “技巧”是以允许您执行范围搜索的方式构建成员(如果愿意,则执行2级失效)。以下是它应该如何显示的示例(请注意,这里的关键ProductA不是Hash而是排序集):

ZADD ProductA 0 0001:0001:<JSON>

要阅读自定义版本,请使用ZRANGEBYLEX ProductA [0001:0001: [0001:0001:\xff并从回复中拆分JSON并删除整个自定义,请使用ZREMRANGEBYLEX