dot Redis 8 来了——而且是开源的

了解更多

Devops 的 Redis 常见难题 – 客户端缓冲区

Redis 提供了各种工具,旨在改进和维护高效的内存数据库使用。 虽然其独特的数据类型和命令可以微调数据库以服务于应用程序请求,而无需在应用程序级别进行任何额外处理,但错误配置,或者更确切地说,使用开箱即用的配置,可能会(并且确实)导致运营挑战和性能问题。

尽管存在一些挫折,这些挫折造成了不少麻烦,但解决方案确实存在,甚至可能比预期的更简单。

本系列文章将重点介绍使用 Redis 时出现的一些最令人恼火的问题,以及如何解决这些问题的提示。 它们基于我们运行数千个 Redis 数据库实例的实际经验。

本系列之前的文章讨论了 Redis 的复制 缓冲区超时。 在这篇文章中,我们将向您介绍 Redis 维护的另一种类型的缓冲区,即客户端缓冲区。 在某些情况下,如果不加以控制,这个家伙可能会成为许多麻烦的根源。

客户端缓冲区

您可能已经知道,Redis 是一个内存数据库,这意味着所有数据都直接从 RAM 进行管理和服务。 这使得 Redis 能够提供无与伦比的性能,以亚毫秒级的延迟提供成千上万的请求。 迄今为止,RAM 是技术提供的最快的存储方式 - 要了解延迟数字,请查看以下内容

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

Notes
-----
1 ns = 10^-9 seconds
1 us = 10^-6 seconds = 1,000 ns
1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns

Credit
------
By Jeff Dean:               http://research.google.com/people/jeff/
Originally by Peter Norvig: http://norvig.com/21-days.html#answers

Contributions
-------------
'Humanized' comparison:  https://gist.github.com/hellerbarde/2843375
Visual comparison chart: http://i.imgur.com/k0t1e.png

Redis,顾名思义和设计,是一个远程服务器,这意味着客户端(通常)通过网络连接到它。 在这种情况下,客户端的请求返回到客户端所需的时间将明显多于 Redis 的 CPU 从 RAM 中实际获取数据的时间。 这种数量级差异的直接含义是,如果不是客户端缓冲区,Redis 将在这段时间内一直忙于处理请求。

客户端缓冲区构成一个内存空间,该内存空间被分配用于服务客户端请求,并且每个与 Redis 的连接都被分配了自己的缓冲区空间。 在处理完请求后,Redis 将响应数据复制到客户端缓冲区,并继续处理后续请求,而请求客户端以其自身网络决定的速度通过该连接读回数据。 Redis 的客户端缓冲区在 redis.conf 文件中通过 client-output-buffer-limit normal 指令进行配置(您可以使用 config get client-output-buffer-limit 在运行时获取此设置)。 默认的 redis.conf 文件将其定义如下

client-output-buffer-limit normal 0 0 0

这些值分别代表缓冲区的软限制、硬限制和超时(以秒为单位)(类似于 复制缓冲区的行为)。 它们起到保护作用,当缓冲区的大小达到 a) 软限制并保持到超时到期或 b) 硬限制时,Redis 将终止连接 - 而不允许客户端读取回复。 将这些限制设置为 0 意味着禁用该保护。

但是,与复制缓冲区不同,客户端缓冲区的内存分配是从 Redis 的数据内存空间中获取的。 Redis 可以使用的总内存量由 maxmemory 指令设置,一旦达到,Redis 将采用其配置的逐出策略(由 maxmemory-policy 指令定义)。 这实际上意味着,性能低下的客户端和/或大量并发连接可能会导致您的 Redis 实例过早地逐出密钥,或者由于其内存使用量(主要是数据集的大小和客户端缓冲区的总和)已达到内存限制,因此拒绝使用内存不足消息 (OOM) 进行更新。

由于生活的相对性,客户端不一定必须很慢才能触发此行为。 由于访问 RAM 和从网络读取之间的速度差异巨大,因此即使使用性能最佳的客户端和网络链接,通过膨胀的客户端缓冲区耗尽 Redis 的内存实际上也很容易实现。 例如,考虑(邪恶的)KEYS 命令,一旦发出,Redis 会将整个密钥命名空间复制到客户端缓冲区。 如果您的数据库具有大量密钥,仅此一项就足以触发逐出。

警告:使用 KEYS 时要格外小心,切勿在生产环境中使用。 除了触发上述逐出的可能性之外,使用它您可能会冒着在很长一段时间内阻止 Redis 的风险。

但是,KEYS 不是唯一会导致这种情况的命令。 同样,如果您的值(和范围)足够大,或者由于每个连接都需要一个单独的缓冲区,如果您有多个打开的连接,Redis 的 SMEMBERS、 HGETALLLRANGEZRANGE(和相关命令)可能会产生相同的效果。

因此,强烈建议不要不负责任地使用这些命令。 相反,首选自 v2.8 以来可用的 SCAN 系列命令。 这些命令不仅允许 Redis 在后续 SCAN 调用之间继续处理请求,而且还降低了耗尽客户端缓冲区的机会。

客户端缓冲区是 Redis 内存需求和管理中经常被忽视的一个方面。 它们的默认设置(即禁用)非常危险,因为它可能很快导致内存耗尽。 通过相应地设置缓冲区的阈值 - 考虑到“maxmemory”设置、现有和预测的内存使用情况以及应用程序的流量模式。 负责任地使用上述命令可以防止令人不快的意外情况,这些情况会让您头痛不已。 请继续关注我们 Devops 的 Redis 常见难题的下一期!