Redis管道,处理缓存未命中

时间:2017-05-31 14:49:43

标签: caching redis pipelining

我正在试图找出实现Redis流水线的最佳方法。我们使用redis作为MySQL之上的缓存来存储用户数据,产品列表等。 我将此作为起点:https://joshtronic.com/2014/06/08/how-to-pipeline-with-phpredis/

我的问题是,假设您有一组正确排序的ID。你像这样循环遍历redis管道:

$redis = new Redis();

// Opens up the pipeline
$pipe = $redis->multi(Redis::PIPELINE);

// Loops through the data and performs actions
foreach ($users as $user_id => $username)
{
    // Increment the number of times the user record has been accessed
    $pipe->incr('accessed:' . $user_id);

    // Pulls the user record
    $pipe->get('user:' . $user_id);
}

// Executes all of the commands in one shot
$users = $pipe->exec();

$pipe->get('user:' . $user_id);不可用时会发生什么情况,因为之前没有被请求过或被Redis驱逐等等?假设结果#15来自50,我们如何a)发现我们无法检索该对象并且b)保持用户数组正确排序?

谢谢

1 个答案:

答案 0 :(得分:2)

我将回答有关Redis协议的问题。在这种情况下,它在特定语言中的工作方式或多或少相同。

首先,让我们看一下Redis管道的工作原理: 它只是一种向服务器发送多个命令,执行它们并获得多个回复的方法。没有什么特别之处,您只需获得一个数组,其中包含管道中每个命令的回复。

为什么流水线要快得多是因为保存了每个命令的往返时间,即100个命令只有一个往返时间而不是100个。此外,Redis同步执行每个命令。执行100个命令需要潜在100次,因为Redis选择那个单一命令,管道被视为一个长命令,因此只需要等待一次同步选择。

您可以在此处详细了解管道传输:https://redis.io/topics/pipelining。还有一点需要注意,因为每个流水线批处理都是不间断运行的(就Redis而言),以可浏览的块发送这些命令是有意义的,即不要在单个管道中发送100k命令,这可能会长时间阻塞Redis,将它们分成1k或10k命令的块。

在你的情况下,你在循环中运行以下片段:

// Increment the number of times the user record has been accessed
$pipe->incr('accessed:' . $user_id);

// Pulls the user record
$pipe->get('user:' . $user_id);

问题是投入管道的是什么?假设您要将u1u2u3u4的数据更新为用户ID。因此,具有Redis命令的管道将如下所示:

INCR accessed:u1
GET user:u1
INCR accessed:u2
GET user:u2
INCR accessed:u3
GET user:u3
INCR accessed:u4
GET user:u4

让我们说:

    之前
  • u1被访问了100次,
  • 之前
  • u2被访问了5次,
  • 之前没有访问
  • u3和
  • u4和附带的数据不存在。

在这种情况下,结果将是Redis回复的数组:

101
u1 string data stored at user:u1
6
u2 string data stored at user:u2
1
u3 string data stored at user:u3
1
NIL

如您所见,Redis会将缺失的INCR值视为0并执行incr(0)。最后,Redis没有对任何内容进行排序,结果将根据要求提供给oder。

语言绑定,例如Redis驱动程序将只解析该协议并为解析数据提供视图。在不保留命令的情况下,Redis驱动程序无法正常工作,并且作为程序员可以推断出smth。请注意,该请求不会在回复中重复,即您在执行u1时不会收到u2GET的密钥,而只会收到该密钥的数据。因此,您的实现必须记住位置1(基于零的索引)是GET u1的结果。