Redis CRUD 模式

dev*_*dev 5 php nosql redis

我最近开始学习 Redis,目前正在构建一个使用它作为唯一数据存储的应用程序,我想与其他 Redis 用户核对我的一些结论是否正确并提出一些问题。如果相关,我正在使用 phpredis,但我想这些问题应该适用于任何语言,因为它更像是一种模式。

例如,考虑一个 CRUD 接口来保存具有以下要求的网站(名称和域):

  • 保存/验证新站点时检查现有名称/域(重复检查)
  • 列出所有带有排序和分页的网站

我最初选择了以下“架构”来保存此信息:

  • 一个关键的“前缀:website_ids”,我使用 INCR 来生成新的网站 ID
  • 一组“前缀:wslist”,其中我添加了上面生成的网站 ID
  • 每个网站“前缀:ws:ID”的哈希值,带有字段名称和网站

保存/验证问题

仅凭上述信息,我无法(据我所知)在添加新网站时检查重复的名称或域。为了解决这个问题,我做了以下工作:

  • 两组键为“prefix:wsnames”和“prefix:wsdomains”,其中我还添加了网站名称和域。

这样,在添加新网站时,我可以使用 SISMEMBER 检查提交的名称或域是否已经存在于这些集合中,并在需要时验证失败。现在,如果我用 50 个字段而不是 2 个字段保存数据,并且想要防止重复,我必须为我想要验证的每个字段创建一个类似的集合。

问题 1:以上是解决此问题的常见模式,还是人们使用其他/更好的方法来解决此类问题?

列表/排序问题

要列出网站并按名称或域(升序或降序)排序以及限制分页结果,我使用以下内容:

SORT prefix:wslist BY prefix:ws:*->name ALPHA ASC LIMIT 0 10
Run Code Online (Sandbox Code Playgroud)

这给了我 10 个按名称排序的网站 ID。现在为了获得这些结果,我选择了以下选项(php 中的示例):

选项1:

$wslist = the sort command here;
$websites = array();
foreach($wslist as $ws) {
    $websites[$ws] = $redis->hGetAll('prefix:ws:'.$ws);
}
Run Code Online (Sandbox Code Playgroud)

上面给了我一个可用的数组,以网站 id 为键和一个字段数组。不幸的是,这有一个问题,即我在循环内对 redis 执行多个请求,常识(至少来自 RDBM)告诉我这不是最佳的。更好的方法似乎是使用 redis pipelining/multi 并一次性发送所有请求:

选项 2:

$wslist = the sort command here;
$redis->multi();
foreach($wslist as $ws) {
    $redis->hGetAll('prefix:ws:'.$ws);
}
$websites = $redis->exec();
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于,现在我无法获得每个网站各自的 ID,除非我再次循环 $websites 数组以关联每个网站。另一种选择是可能还保存一个字段“id”,其中包含哈希本身内的相应网站 id 以及名称和域。

问题 2/3:在一个可用数组中获得这些结果而不必多次循环的最佳方法是什么?将 id 号也保存为哈希中的一个字段以便我也可以通过结果获取它是正确的还是好的做法?

免责声明:我知道使用像 Redis 这样的键->值数据存储时的编码和模式构建范式与 RDBM 和文档存储不同,因此“执行 X 的最佳方法”的概念可能因数据和应用程序而异在眼前。我也明白 Redis 甚至可能不是最适合在大多数 CRUD 类型应用程序中使用的数据存储,但我仍然希望从更有经验的开发人员那里获得任何见解,因为 CRUD 接口在大多数应用程序中非常普遍。

Car*_*auf 2

答案1

你的建议看起来很常见。我不确定为什么你需要一个自动递增的 ID。我认为域名必须是唯一的,或者网站名称必须是唯一的,或者至少两者的组合必须是唯一的。如果是这种情况,听起来您已经拥有了一个完美的密钥,那么当您不需要它时为什么要发明一个整数密钥呢?

拥有域名 SET 和网站名称 SET 是快速检查特定域名或网站名称是否已存在的完美解决方案。不过,如果其中之一(域名或网站名称)是您的密钥,您甚至可能不需要这些 SET,因为您只需查看该密钥是否prefix:ws:domain-or-ws-name-here存在即可。

此外,为每个网站使用哈希,以便您可以在内部存储网站的 50 个详细信息字段,这是完美的。这就是哈希的用途。

答案2

首先,让我指出,如果您的网站和域名存储在 SORTED SET 而不是 SET 中,则它们已经按字母顺序排列(假设它们具有相同的分数)。如果您尝试支持其他排序选项,这可能没有多大帮助,但想指出这一点。

你的方案1和方案2其实都比较合理。Redis 速度快如闪电,因此选项 1 并不像乍看起来那么不合理。从 redis 的角度来看,选项 2 显然更加优化,因为所有命令都将被缓冲并立即执行。不过,正如您所指出的,如果您希望数组通过 id 进行索引,之后将需要在 PHP 中进行额外的处理。

还有第三个选项:lua 脚本。您可以让 Redis 执行一个 Lua 脚本,一次性返回 ids 和哈希值。但是,我不再非常熟悉 PHP 以及 Redis 的多字节回复如何映射到 PHP 数组,我不能 100% 确定 lua 脚本会是什么样子。您需要寻找示例或进行一些试验和错误。不过,这应该是一个非常简单的脚本。

结论

我认为 redis 听起来是解决你的问题的一个不错的解决方案。请记住,数据集必须始终足够小才能保留在内存中。如果这不是一个真正的问题(除非您的字段很大,您应该能够在几 MB 的空间中容纳数千个网站),或者如果您不介意升级 RAM 来扩展数据库,那么 Redis 非常适合。

熟悉 Redis 的各种持久性选项和配置以及它们对可用性和可靠性的含义。另外,请确保您有适当的备份解决方案。我建议同时拥有一个从属于主实例的辅助 Redis 实例,以及一个至少每天备份 Redis 数据库文件的重复进程。