【问题标题】:How to improve Redis server's CPU usage?如何提高 Redis 服务器的 CPU 使用率?
【发布时间】:2013-04-19 01:03:13
【问题描述】:

我的目标是让我们的 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依赖于hiredisredis-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?

【问题讨论】:

    标签: lua redis benchmarking


    【解决方案1】:

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

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

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

    现在,如果您真的想增加 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 饱和的示例。

    如果还没有完成,我建议你先看看the Redis benchmark page

    如果您在与 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 客户端:

    您可能想尝试一下。

    【讨论】:

      猜你喜欢
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      • 2014-07-13
      • 2018-07-08
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多