4

我的目标是让我们的 Redis 服务器在生产中达到大约 80% 的 CPU 利用率。这将有利于我们的后端服务器设计,确保我们不会未充分利用 CPU,同时也为增长和峰值留出一些空间。

在使用 Redis 自己的基准测试工具redis-benchmark时,很容易达到大约 100% 的 CPU 使用率:

    $ redis-benchmark -h 192.168.1.6 -n 1000000 -c 50

在这个基准测试中,我们分配了 50 个客户端向我们的 Redis 服务器推送 1,000,000 个请求。

但是在使用其他一些客户端工具(如redis-luawebdis)时,最大 CPU 使用率低于 60%。

我浏览了一些代码webdisredis-lua. webdis依赖于hiredis,并redis-lua在 Lua 中实现,依赖于 socket( lua-socket)。

与 Redis 基准测试相比,这些客户端是否太慢并且无法最大化 Redis CPU 消耗?

我还浏览了redis-benchmark.c. 基准测试的主要工作是在aeMain. 似乎redis-benchmark使用来自 Redis 的快速代码,而我的测试客户端(webdisredis-lua)没有。

目前我的客户有两种选择:

  1. 利用redis-lua
  2. 使用类似的工具webdis

但是,这两个并没有最大化 Redis 的 CPU 利用率(低于 60%)。还有其他选择吗?

或者,是否可以在redis-benchmark工具本身之外充分利用 redis-server?

4

1 回答 1

9

我怀疑最大化 Redis 的 CPU 使用率是否会有益于您的后端设计。正确的问题是 Redis 是否足够高效以在给定的延迟下维持吞吐量。Redis 是单线程服务器:在 80% 的 CPU 消耗下,延迟可能会非常糟糕。

我建议您在 redis-benchmark 工作时测量延迟,以查看它是否可以满足您的需求,然后再尝试增加 Redis CPU 消耗。redis-cli 的 --latency 选项可用于此:

  • 启动 redis 服务器
  • 尝试 redis-cli --latency,记下 avg 值,停止它
  • 在另一个窗口中,启动基准测试,并确保它运行一段时间
  • 尝试 redis-cli --latency,记下 avg 值,停止它
  • 停止基准测试
  • 比较两个平均值

现在,如果你真的想增加 Redis CPU 消耗,你需要一个高效的客户端程序(如 redis-benchmark),能够同时处理多个连接,或者客户端程序的多个实例。

Lua 是一种快速解释型语言,但它仍然是一种解释型语言。它会比 C 代码慢一到两个数量级。Redis 在解析/生成其协议方面比 lua-redis 快得多,因此您将无法使用唯一的 Lua 客户端使 Redis 饱和(除非您使用 O(n) Redis 命令 - 见下文)。

webdis 是用 C 实现的,具有高效的客户端库,但必须解析 http/json 协议,这恰好比 Redis 协议更冗长和复杂。对于大多数操作,它可能比 Redis 本身消耗更多的 CPU。同样,您不会用单个 webdis 实例使 Redis 饱和。

下面是一些使用多个 Lua 客户端使 Redis 饱和的示例。

如果尚未完成,我建议您先查看Redis 基准页面

如果您在与 Redis 相同的机器上运行基准测试:

关键是将一个核心专用于Redis,并在其他核心上运行客户端程序。在 Linux 上,您可以为此使用 taskset 命令。

# Start Redis on core 0
taskset -c 0 redis-server redis.conf

# Start Lua programs on the other cores
for x in `seq 1 10` ; do taskset -c 1,2,3 luajit example.lua & done

Lua 程序应该使用流水线来最大化吞吐量并减少系统活动。

local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)
for i=1,1000000 do
    local replies = client:pipeline(function(p)
    for j=1,1000 do
            local key = 'counter:'..tostring(j)
            p:incrby(key,1)
        end
    end)
end

在我的系统上,Lua 程序占用的 CPU 是 Redis 的 4 倍多,因此使用这种方法需要 4 个以上的核心才能使 Redis 饱和(6 核心的盒子应该没问题)。

如果您在与 Redis 不同的机器上运行基准测试:

除非您在 CPU 匮乏的虚拟机上运行,​​否则在这种情况下,瓶颈很可能是网络。我不认为你可以用任何小于 1 GbE 的链接使 Redis 饱和。

确保尽可能地管道化查询(参见前面的 Lua 程序)以避免网络延迟瓶颈,并降低 CPU 上的网络中断成本(填充以太网数据包)。尝试在未绑定到网卡(并处理网络中断)的核心上运行 Redis。您可以使用 htop 之类的工具来检查最后一点。

如果可以,请尝试在网络的其他各种机器上运行您的 Lua 客户端。同样,您将需要大量 Lua 客户端来使 Redis 饱和(6-10 应该没问题)。

在某些情况下,一个独特的 Lua 进程就足够了:

现在,如果每个查询都足够昂贵,则可以使用单个 Lua 客户端使 Redis 饱和。这是一个例子:

local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)

for i=1,1000 do
    local replies = client:pipeline(function(p)
        for j=1,1000 do
            p:rpush("toto",i*1000+j)
        end
    end)
end

N = 500000
for i=1,100000 do
    local replies = client:pipeline(function(p)
        for j=1,10 do
            p:lrange("toto",N, N+10)
        end
    end)
end

这个程序用 1M 项填充一个列表,然后使用 lrange 命令从列表中间获取 10 个项目(Redis 的最坏情况)。因此,每次执行查询时,服务器都会扫描 500K 项。因为只返回了 10 个项目,所以它们被 lua-redis 快速解析,不会消耗 CPU。在这种情况下,所有的 CPU 消耗都将在服务器端。

最后的话

可能有比 redis-lua 更快的 Redis 客户端:

您可能想尝试一下。

于 2013-04-25T12:19:37.323 回答