诊断延迟问题

查找响应缓慢的原因

如果您遇到 Redis 延迟问题,本文档将帮助您了解可能出现的问题。

在此上下文中,延迟是指客户端发出命令和客户端收到对该命令的答复之间的时间的最大延迟。通常,Redis 的处理时间极短,在微秒范围内,但某些条件会导致更高的延迟数字。

时间紧迫,给我一份清单

以下文档对于以低延迟方式运行 Redis 非常重要。但是,我理解我们都很忙,所以让我们从一个快速清单开始。如果您无法按照这些步骤操作,请返回此处阅读完整文档。

  1. 确保您没有运行会阻塞服务器的慢命令。使用 Redis 慢日志功能 进行检查。
  2. 对于 EC2 用户,请确保使用基于 HVM 的现代 EC2 实例,例如 m3.medium。否则,fork() 会太慢。
  3. 必须从内核中禁用透明大页面。使用 echo never > /sys/kernel/mm/transparent_hugepage/enabled 禁用它们,然后重新启动 Redis 进程。
  4. 如果您使用的是虚拟机,则您可能存在与 Redis 无关的固有延迟。使用 ./redis-cli --intrinsic-latency 100 检查运行时环境中可以预期的最小延迟。注意:您需要在服务器中运行此命令,而不是在客户端中运行。
  5. 启用并使用 Redis 的延迟监视器功能,以便获得 Redis 实例中延迟事件和原因的人类可读描述。

一般来说,使用下表了解耐用性与延迟/性能权衡,从更强的安全性到更好的延迟排序。

  1. 始终使用 AOF + fsync:这非常慢,只有在您知道自己在做什么时才应使用它。
  2. 每秒 AOF + fsync:这是一个不错的折衷方案。
  3. 每秒 AOF + fsync + 将 no-appendfsync-on-rewrite 选项设置为 yes:这与上述相同,但避免在重写期间 fsync 以降低磁盘压力。
  4. 从不使用 AOF + fsync。在这种设置中,fsyncing 取决于内核,磁盘压力和延迟峰值风险更低。
  5. RDB。根据您配置的保存触发器,您在这里拥有广泛的权衡范围。

现在,对于有 15 分钟时间的人来说,详细内容...

测量延迟

如果您遇到延迟问题,您可能知道如何在应用程序上下文中测量它,或者您的延迟问题即使在宏观上也很明显。但是,redis-cli 可用于以毫秒为单位测量 Redis 服务器的延迟,只需尝试

redis-cli --latency -h `host` -p `port`

使用内部 Redis 延迟监视子系统

自 Redis 2.8.13 起,Redis 提供了延迟监视功能,能够对不同的执行路径进行采样,以了解服务器在哪里阻塞。这使得调试本文档中说明的问题变得更加简单,因此我们建议尽快启用延迟监视。请参阅延迟监视器文档

虽然延迟监视采样和报告功能将使了解 Redis 系统中延迟的来源变得更加简单,但我们仍然建议您广泛阅读本文档,以更好地了解 Redis 和延迟峰值主题。

延迟基准

有一种延迟是您运行 Redis 的环境固有的一部分,即您的操作系统内核提供的延迟,如果您使用虚拟化,则由您使用的管理程序提供。

虽然无法消除这种延迟,但研究它很重要,因为它是基准,换句话说,您将无法实现比在您的环境中运行的每个进程由于内核或管理程序实现或设置而经历的延迟更好的 Redis 延迟。

我们称这种延迟为固有延迟,而从 Redis 2.8.7 版本开始,redis-cli 能够测量它。这是在运行在入门级服务器上的 Linux 3.11.0 下运行的一个示例。

注意:参数 100 是测试将执行的秒数。我们运行测试的时间越长,我们就越有可能发现延迟峰值。100 秒通常是合适的,但是你可能希望在不同的时间进行多次运行。请注意,该测试是 CPU 密集型的,并且可能会使系统中的单个内核饱和。

$ ./redis-cli --intrinsic-latency 100
Max latency so far: 1 microseconds.
Max latency so far: 16 microseconds.
Max latency so far: 50 microseconds.
Max latency so far: 53 microseconds.
Max latency so far: 83 microseconds.
Max latency so far: 115 microseconds.

注意:在这种特殊情况下,redis-cli 需要在服务器中运行,而不是在客户端中运行或计划运行 Redis。在这种特殊模式下,redis-cli 根本不会连接到 Redis 服务器:它只会尝试测量内核不为 redis-cli 进程本身提供 CPU 时间运行的最长时间。

在上面的示例中,系统的固有延迟仅为 0.115 毫秒(或 115 微秒),这是一个好消息,但请记住,固有延迟可能会随着系统负载而随时间变化。

虚拟化环境不会显示如此好的数字,尤其是在高负载或有嘈杂邻居的情况下。以下是运行在运行 Redis 和 Apache 的 Linode 4096 实例上的运行结果

$ ./redis-cli --intrinsic-latency 100
Max latency so far: 573 microseconds.
Max latency so far: 695 microseconds.
Max latency so far: 919 microseconds.
Max latency so far: 1606 microseconds.
Max latency so far: 3191 microseconds.
Max latency so far: 9243 microseconds.
Max latency so far: 9671 microseconds.

在这里,我们的固有延迟为 9.7 毫秒:这意味着我们无法要求 Redis 比这更好。然而,在具有更高负载或嘈杂邻居的不同虚拟化环境中在不同时间进行的其他运行很容易显示出更差的值。我们能够在其他方面明显正常运行的系统中测量到高达 40 毫秒的延迟。

由网络和通信引起的延迟

客户端通过 TCP/IP 连接或 Unix 域连接连接到 Redis。1 Gbit/s 网络的典型延迟约为 200 微秒,而 Unix 域套接字的延迟低至 30 微秒。这实际上取决于您的网络和系统硬件。除了通信本身之外,系统还增加了更多延迟(由于线程调度、CPU 缓存、NUMA 放置等)。虚拟化环境中由系统引起的延迟明显高于物理机。

即使 Redis 在亚微秒范围内处理大多数命令,执行多次往返服务器的客户端也必须支付这些网络和系统相关的延迟。

因此,高效的客户端将尝试通过将多个命令一起管道化来限制往返次数。服务器和大多数客户端都完全支持这一点。聚合命令(如 MSET/MGET)也可以用于此目的。从 Redis 2.4 开始,许多命令还支持所有数据类型可变参数。

以下是一些准则

  • 如果您负担得起,请优先选择物理机而不是虚拟机来托管服务器。
  • 不要系统地连接/断开与服务器的连接(尤其适用于基于 Web 的应用程序)。尽可能保持您的连接长期存在。
  • 如果您的客户端与服务器在同一主机上,请使用 Unix 域套接字。
  • 优先使用聚合命令(MSET/MGET)或具有可变参数的命令(如果可能),而不是管道化。
  • 优先使用管道化(如果可能),而不是往返序列。
  • Redis 支持 Lua 服务器端脚本,以涵盖不适合原始管道化的案例(例如,当命令的结果是后续命令的输入时)。

在 Linux 上,一些人可以通过处理放置(taskset)、cgroups、实时优先级(chrt)、NUMA 配置(numactl)或使用低延迟内核来实现更好的延迟。请注意,原生的 Redis 并不适合绑定在单个 CPU 内核上。Redis 可以派生后台任务,这些任务可能极度消耗 CPU,例如 BGSAVEBGREWRITEAOF。这些任务绝不能与主事件循环在同一内核上运行。

在大多数情况下,不需要此类系统级优化。只有在需要时才执行它们,并且如果您熟悉它们。

Redis 的单线程特性

Redis 使用几乎单线程设计。这意味着单个进程使用称为多路复用的技术来处理所有客户端请求。这意味着 Redis 可以在任何给定时刻处理单个请求,因此所有请求都是按顺序处理的。这与 Node.js 的工作方式非常相似。但是,这两个产品通常不会被认为是慢的。这部分原因是完成单个请求所需的时间很短,但主要原因是这些产品被设计为不会阻塞系统调用,例如从套接字读取数据或向套接字写入数据。

我说 Redis 是几乎单线程的,因为实际上从 Redis 2.4 开始,我们在 Redis 中使用线程在后台执行一些慢速 I/O 操作,主要与磁盘 I/O 相关,但这并不会改变 Redis 使用单个线程处理所有请求的事实。

慢速命令产生的延迟

单线程的一个后果是,当一个请求处理缓慢时,所有其他客户端都将等待此请求得到处理。执行正常命令时,例如 GETSETLPUSH,这不是问题,因为这些命令在恒定(且非常短)的时间内执行。但是,有一些命令操作许多元素,例如 SORTLREMSUNION 等。例如,求两个大集合的交集可能需要相当长的时间。

所有命令的算法复杂度都有记录。一个好习惯是在使用不熟悉的命令时系统地检查它。

如果您担心延迟,则不应针对由许多元素组成的值使用慢速命令,或者您应该运行使用 Redis 复制的副本,在该副本中运行所有慢速查询。

可以使用 Redis 慢日志功能 监视慢速命令。

此外,您可以使用您最喜欢的按进程监视程序(top、htop、prstat 等)快速检查 Redis 主进程的 CPU 消耗。如果在流量不高的情况下 CPU 消耗很高,这通常表示使用了慢速命令。

重要提示:由慢速命令执行产生的延迟的一个非常常见的来源是在生产环境中使用 KEYS 命令。正如 Redis 文档中所述,KEYS 应该仅用于调试目的。自 Redis 2.8 起,引入了新的命令来增量迭代键空间和其他大型集合,请查看 SCANSSCANHSCANZSCAN 命令以了解更多信息。

fork 产生的延迟

为了在后台生成 RDB 文件,或者在启用 AOF 持久性时重写仅附加文件,Redis 必须派生后台进程。派生操作(在主线程中运行)本身可能会导致延迟。

在大多数类 Unix 系统上,派生是一项昂贵的操作,因为它涉及复制与进程链接的大量对象。对于与虚拟内存机制关联的页面表尤其如此。

例如,在 Linux/AMD64 系统上,内存被划分为 4 kB 页面。为了将虚拟地址转换为物理地址,每个进程都存储一个页面表(实际上表示为树),其中包含进程地址空间中每个页面的至少一个指针。因此,一个 24 GB 的大型 Redis 实例需要一个 24 GB / 4 kB * 8 = 48 MB 的页面表。

当执行后台保存时,必须派生此实例,这将涉及分配和复制 48 MB 内存。这需要时间和 CPU,尤其是在虚拟机上,其中分配和初始化大块内存可能很昂贵。

不同系统中的派生时间

现代硬件在复制页面表方面相当快,但 Xen 则不然。Xen 的问题不是虚拟化特有的,而是 Xen 特有的。例如,使用 VMware 或 Virtual Box 不会导致派生时间变慢。下表比较了不同 Redis 实例大小的派生时间。通过执行 BGSAVE 并查看 INFO 命令输出中的 latest_fork_usec 字段来获取数据。

然而,好消息是基于 EC2 HVM 的新型实例在派生时间方面要好得多,几乎与物理服务器相当,因此例如使用 m3.medium(或更好)实例将提供良好的结果。

  • VMware 上的 Linux 健壮 VM 6.0GB RSS 在 77 毫秒内派生(每 GB 12.8 毫秒)。
  • 在物理机上运行的 Linux(未知硬件) 6.1GB RSS 在 80 毫秒内派生(每 GB 13.1 毫秒)。
  • 在物理机上运行的 Linux(Xeon @ 2.27Ghz) 6.9GB RSS 在 62 毫秒内派生(每 GB 9 毫秒)。
  • 6sync(KVM)上的 Linux VM 360 MB RSS 在 8.2 毫秒内派生(每 GB 23.3 毫秒)。
  • EC2 上的 Linux VM,旧实例类型(Xen) 6.1GB RSS 在 1460 毫秒内派生(每 GB 239.3 毫秒)。
  • EC2 上的 Linux VM,新实例类型(Xen) 1GB RSS 在 10 毫秒内派生(每 GB 10 毫秒)。
  • Linode 上的 Linux 虚拟机 (Xen) 0.9GBRSS 在 382 毫秒内分叉(每 GB 424 毫秒)。

正如您所见,在 Xen 上运行的某些虚拟机存在性能损失,其数量级在 1 到 2 个数量级之间。对于 EC2 用户,建议很简单:使用基于 HVM 的现代实例。

透明大页面导致的延迟

不幸的是,当 Linux 内核启用了透明大页面时,Redis 在使用 fork 调用以保留在磁盘上后会产生较大的延迟损失。大页面是导致以下问题的原因

  1. 调用 Fork,创建了两个具有共享大页面的进程。
  2. 在繁忙的实例中,一些事件循环运行将导致命令针对数千个页面,从而导致几乎整个进程内存的写时复制。
  3. 这将导致较大的延迟和较大的内存使用量。

请务必使用以下命令禁用透明大页面

echo never > /sys/kernel/mm/transparent_hugepage/enabled

交换(操作系统分页)导致的延迟

Linux(和许多其他现代操作系统)能够将内存页面从内存重新分配到磁盘,反之亦然,以便有效地使用系统内存。

如果内核将 Redis 页面从内存移到交换文件,当 Redis 使用存储在此内存页面中的数据(例如访问存储在此内存页面中的键)时,内核将停止 Redis 进程以将页面移回主内存。这是一个涉及随机 I/O 的缓慢操作(与访问已在内存中的页面相比),并且会导致 Redis 客户端遇到异常延迟。

内核将 Redis 内存页面重新分配到磁盘主要有三个原因

  • 系统处于内存压力之下,因为正在运行的进程要求的物理内存多于可用内存。此问题的最简单实例就是 Redis 使用的内存多于可用内存。
  • Redis 实例数据集或数据集的一部分大部分完全处于空闲状态(从未被客户端访问),因此内核可以将空闲内存页面交换到磁盘上。此问题非常罕见,因为即使是中等速度的实例也会经常接触所有内存页面,从而迫使内核将所有页面保留在内存中。
  • 一些进程在系统上生成大量读取或写入 I/O。由于文件通常被缓存,因此它往往会给内核施加压力以增加文件系统缓存,从而产生交换活动。请注意,它包括可以生成大文件的 Redis RDB 和/或 AOF 后台线程。

幸运的是,Linux 提供了很好的工具来调查问题,因此当怀疑是由于交换而导致延迟时,最简单的方法就是检查是否属实。

首先要检查交换到磁盘上的 Redis 内存量。为此,您需要获取 Redis 实例 pid

$ redis-cli info | grep process_id
process_id:5454

现在进入此进程的 /proc 文件系统目录

$ cd /proc/5454

您将在此处找到一个名为 smaps 的文件,该文件描述了 Redis 进程的内存布局(假设您使用的是 Linux 2.6.16 或更高版本)。此文件包含有关我们的进程内存映射的非常详细的信息,而名为 Swap 的一个字段正是我们正在寻找的。但是,不仅仅有一个交换字段,因为 smaps 文件包含我们 Redis 进程的不同内存映射(进程的内存布局比简单的线性页面数组更复杂)。

由于我们对进程交换的所有内存感兴趣,因此首先要对整个文件中的 Swap 字段进行 grep

$ cat smaps | grep 'Swap:'
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                 12 kB
Swap:                156 kB
Swap:                  8 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB

如果所有内容都是 0 kB,或者有零星的 4k 条目,则一切正常。实际上,在我们的示例实例(运行 Redis 并每秒为数百名用户提供服务的真实网站)中,有一些条目显示更多交换的页面。为了调查这是否是一个严重的问题,我们更改了命令以打印内存映射的大小

$ cat smaps | egrep '^(Swap|Size)'
Size:                316 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  0 kB
Size:                 40 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:             720896 kB
Swap:                 12 kB
Size:               4096 kB
Swap:                156 kB
Size:               4096 kB
Swap:                  8 kB
Size:               4096 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:               1272 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                 16 kB
Swap:                  0 kB
Size:                 84 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  4 kB
Size:                  8 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  4 kB
Size:                144 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  4 kB
Size:                 12 kB
Swap:                  4 kB
Size:                108 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                272 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB

正如您从输出中看到的那样,有一个 720896 kB 的映射(仅交换了 12 kB)和另一个映射中交换的 156 kB:基本上,我们内存中交换的量非常小,因此这根本不会造成任何问题。

如果进程内存的非平凡量反而交换到磁盘上,则您的延迟问题可能与交换有关。如果您的 Redis 实例出现这种情况,您可以使用 vmstat 命令进一步验证

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0  0   3980 697932 147180 1406456    0    0     2     2    2    0  4  4 91  0
 0  0   3980 697428 147180 1406580    0    0     0     0 19088 16104  9  6 84  0
 0  0   3980 697296 147180 1406616    0    0     0    28 18936 16193  7  6 87  0
 0  0   3980 697048 147180 1406640    0    0     0     0 18613 15987  6  6 88  0
 2  0   3980 696924 147180 1406656    0    0     0     0 18744 16299  6  5 88  0
 0  0   3980 697048 147180 1406688    0    0     0     4 18520 15974  6  6 88  0
^C

输出中对我们来说有趣的部分是 siso 两个列,它们计算从交换文件交换的内存量。如果您在这些两列中看到非零计数,则系统中存在交换活动。

最后,iostat 命令可用于检查系统的全局 I/O 活动。

$ iostat -xk 1
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          13.55    0.04    2.92    0.53    0.00   82.95

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda               0.77     0.00    0.01    0.00     0.40     0.00    73.65     0.00    3.62   2.58   0.00
sdb               1.27     4.75    0.82    3.54    38.00    32.32    32.19     0.11   24.80   4.24   1.85

如果您的延迟问题是由于 Redis 内存交换到磁盘上,则需要降低系统中的内存压力,如果 Redis 使用的内存超过可用内存,则添加更多 RAM 或避免在同一系统中运行其他内存占用较大的进程。

由于 AOF 和磁盘 I/O 而导致的延迟

延迟的另一个来源是由于 Redis 上的仅附加文件支持。AOF 基本上使用两个系统调用来完成其工作。一个是 write(2),用于将数据写入仅附加文件,另一个是 fdatasync(2),用于刷新磁盘上的内核文件缓冲区,以确保用户指定的持久性级别。

write(2) 和 fdatasync(2) 调用都可能是延迟的来源。例如,当系统范围内正在进行同步时,或者当输出缓冲区已满并且内核需要刷新到磁盘以接受新写入时,write(2) 可能会阻塞。

fdatasync(2) 调用是延迟的更糟来源,因为在使用的内核和文件系统的许多组合中,它可能需要几毫秒到几秒才能完成,尤其是在其他进程正在执行 I/O 的情况下。因此,在可能的情况下,Redis 自 2.4 版以来就在不同的线程中执行 fdatasync(2) 调用。

我们来看看在使用 AOF 文件时,配置如何影响延迟的量和来源。

可以使用 appendfsync 配置选项以三种不同的方式将 AOF 配置为在磁盘上执行 fsync(此设置可以使用 CONFIG SET 命令在运行时进行修改)。

  • 当 appendfsync 设置为 no 时,Redis 不执行 fsync。在此配置中,延迟的唯一来源可能是 write(2)。当这种情况发生时,通常没有解决方案,因为磁盘无法应对 Redis 接收数据的速度,但是,如果磁盘没有因其他执行 I/O 的进程而严重变慢,这种情况并不常见。

  • 当 appendfsync 设置为 everysec 时,Redis 每秒执行一次 fsync。它使用不同的线程,如果 fsync 仍在进行中,Redis 将使用缓冲区将 write(2) 调用延迟最多两秒(因为如果针对同一文件正在进行 fsync,则 write 将在 Linux 上阻塞)。但是,如果 fsync 占用时间过长,Redis 最终将执行 write(2) 调用,即使 fsync 仍在进行中,这可能会导致延迟。

  • 当 appendfsync 设置为 always 时,在向客户端回复 OK 代码之前,每次写入操作都会执行 fsync(实际上,Redis 会尝试将同时执行的许多命令聚合到单个 fsync 中)。在此模式下,总体性能非常低,强烈建议使用快速磁盘和可以在短时间内执行 fsync 的文件系统实现。

大多数 Redis 用户将对 appendfsync 配置指令使用 noeverysec 设置。为了实现最低延迟,建议避免在同一系统中执行 I/O 的其他进程。使用 SSD 磁盘也可以提供帮助,但通常即使是非 SSD 磁盘,如果磁盘是备用的,并且 Redis 在写入仅附加文件时不执行任何搜索,也能很好地执行。

如果你想调查与仅附加文件相关的延迟问题,可以在 Linux 下使用 strace 命令

sudo strace -p $(pidof redis-server) -T -e trace=fdatasync

上述命令将显示 Redis 在主线程中执行的所有 fdatasync(2) 系统调用。使用上述命令,你将看不到在将 appendfsync 配置选项设置为 everysec 时由后台线程执行的 fdatasync 系统调用。要做到这一点,只需将 -f 开关添加到 strace 即可。

如果你愿意,还可以使用以下命令同时查看 fdatasync 和 write 系统调用

sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write

但是,由于 write(2) 也用于向客户端套接字写入数据,因此这可能会显示太多与磁盘 I/O 无关的内容。显然,没有办法告诉 strace 只显示慢速系统调用,所以我使用以下命令

sudo strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished

由过期时间生成的延迟

Redis 有两种清除过期键的方式

  • 一种惰性方式在命令请求时使键过期,但发现它已过期。
  • 一种主动方式每 100 毫秒使一些键过期。

主动过期旨在具有适应性。每 100 毫秒(每秒 10 次)启动一个过期周期,并将执行以下操作

  • ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 键进行采样,清除所有已过期的键。
  • 如果发现超过 25% 的键已过期,则重复。

鉴于 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 默认设置为 20,并且该进程每秒执行十次,通常每秒仅主动过期 200 个键。即使已过期的键长时间未被访问,这也足以快速清理数据库,因此惰性算法无济于事。同时,每秒仅过期 200 个键对 Redis 实例的延迟没有影响。

但是,如果算法发现采样键集中超过 25% 的键已过期,则该算法具有适应性并且会循环。但鉴于我们每秒运行算法十次,这意味着超过 25% 的键在我们的随机样本中过期的不幸事件至少在同一秒内发生。

基本上,这意味着如果数据库中有许多键在同一秒内过期,并且这些键至少占当前具有过期设置的键总数的 25%,则 Redis 可以阻塞以使已过期键的百分比低于 25%。

需要这种方法来避免为已过期的键使用过多内存,并且通常绝对无害,因为大量键在同一秒内过期的情况很奇怪,但用户使用 EXPIREAT 与同一 Unix 时间广泛使用并非不可能。

简而言之:请注意,许多键在同一时刻过期可能是延迟的根源。

Redis 软件监视器

Redis 2.6 引入了Redis 软件监视器,这是一个调试工具,旨在跟踪那些由于某种原因而无法使用正常工具进行分析的延迟问题。

软件监视器是一项实验性功能。虽然它设计用于生产环境,但在继续之前应注意备份数据库,因为它可能与 Redis 服务器的正常执行产生意外交互。

重要的是仅在无法通过其他方式跟踪问题时将其用作最后手段

此功能的工作方式如下

  • 用户使用 CONFIG SET 命令启用软件监视。
  • Redis 开始持续监视自身。
  • 如果 Redis 检测到服务器被阻塞在某个操作中,并且该操作无法足够快地返回,并且这可能是延迟问题的原因,那么有关服务器被阻塞位置的低级别报告将转储到日志文件中。
  • 用户联系开发者,在 Redis Google Group 中编写一条消息,并在消息中包含监视报告。

请注意,此功能无法使用 redis.conf 文件启用,因为它设计为仅在已运行的实例中启用,并且仅用于调试目的。

要启用此功能,只需使用以下内容

CONFIG SET watchdog-period 500

时间段以毫秒为单位指定。在上面的示例中,我指定仅在服务器检测到延迟 500 毫秒或更长时间时才记录延迟问题。可配置的最小时间段为 200 毫秒。

当你完成软件监视时,你可以通过将 watchdog-period 参数设置为 0 来关闭它。重要提示:请记住这样做,因为将实例与监视功能保持开启状态的时间超过需要的时间通常不是一个好主意。

以下是软件监视检测到延迟时间超过配置时间后,你将在日志文件中看到打印的示例

[8547 | signal handler] (1333114359)
--- WATCHDOG TIMER EXPIRED ---
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libpthread.so.0(+0xf8f0) [0x7f16b5f158f0]
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libc.so.6(usleep+0x34) [0x7f16b5c62844]
./redis-server(debugCommand+0x3e1) [0x43ab41]
./redis-server(call+0x5d) [0x415a9d]
./redis-server(processCommand+0x375) [0x415fc5]
./redis-server(processInputBuffer+0x4f) [0x4203cf]
./redis-server(readQueryFromClient+0xa0) [0x4204e0]
./redis-server(aeProcessEvents+0x128) [0x411b48]
./redis-server(aeMain+0x2b) [0x411dbb]
./redis-server(main+0x2b6) [0x418556]
/lib/libc.so.6(__libc_start_main+0xfd) [0x7f16b5ba1c4d]
./redis-server() [0x411099]
------

注意:在示例中,DEBUG SLEEP 命令用于阻塞服务器。如果服务器在不同的上下文中阻塞,则堆栈跟踪将不同。

如果你碰巧收集了多个监视堆栈跟踪,建议你将所有内容发送到 Redis Google Group:我们获得的跟踪越多,就越容易了解你的实例存在什么问题。

对本页进行评分