How to improve Redis server's CPU usage?

前端 未结 1 861
遇见更好的自我
遇见更好的自我 2021-02-10 13:50

My goal is for our Redis server to hit about 80% CPU utilization in production. This will benefit our backend server design by ensuring we are not under-utilizing CPU while also

1条回答
  •  逝去的感伤
    2021-02-10 14:20

    I doubt maximizing CPU usage of Redis will benefit your backend design. The right question is rather whether Redis is efficient enough to sustain your throughput at a given latency. Redis is a single-threaded server: at 80% CPU consumption, the latency will likely be very bad.

    I suggest you measure latency while redis-benchmark is working to see if it is acceptable for your needs before trying to increase Redis CPU consumption. The --latency option of redis-cli can be used for this:

    • launch redis-server
    • try redis-cli --latency, note the avg value, stop it
    • in another window, start the benchmark, and make sure it runs for a while
    • try redis-cli --latency, note the avg value, stop it
    • stop the benchmark
    • compare the two avg values

    Now, if you really want to increase Redis CPU consumption, you need either an efficient client program (like redis-benchmark), able to handle multiple connections concurrently, either multiple instances of your client program.

    Lua is a fast interpreted language, but it is still an interpreted language. It will be one or two orders of magnitude slower than C code. Redis is much faster at parsing/generating its protocol than lua-redis, so you will not be able to saturate Redis with a unique Lua client (except if you use O(n) Redis commands - see later).

    webdis is implemented in C, with an efficient client library, but has to parse the http/json protocols which happen to be more verbose and complex than Redis protocol. It likely consumes more CPU than Redis itself for most operations. So again, you will not saturate Redis with a single webdis instance.

    Here are some examples to saturate Redis with multiple Lua clients.

    If it is not already done, I suggest you had a look at the Redis benchmark page first.

    If you run your benchmark on the same box as Redis:

    The key point is to dedicate a core to Redis, and run the client programs on the other cores. On Linux, you can use the taskset command for this.

    # 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
    

    The Lua program should use pipelining to maximize throughput and reduce system activity.

    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
    

    On my system, the Lua program takes more than 4 times the CPU of Redis, so you need more than 4 cores to saturate Redis with this method (a 6 cores box should be fine).

    If you run your benchmark on a different box than Redis:

    Except if you run on CPU starved virtual machines, the bottleneck will likely be the network in that case. I don't think you can saturate Redis with anything less than a 1 GbE link.

    Be sure to pipeline your queries as far as you can (see the previous Lua program) to avoid the network latency bottleneck, and reduce the cost of network interrupts on the CPU (filling ethernet packets). Try to run Redis on a core which is not bound to the network card (and processes network interrupts). You can use tools like htop to check this last point.

    Try to run your Lua clients on various other machines of the network if you can. Again you will need a good number of Lua clients to saturate Redis (6-10 should be fine).

    In some cases, a unique Lua process is enough:

    Now, it is possible to saturate Redis with a single Lua client if each query is expensive enough. Here is an example:

    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
    

    This program populates a list with 1M items, and then uses lrange commands to fetch 10 items from the middle of the list (worst case for Redis). So each time a query is executed, 500K items are scanned by the server. Because only 10 items are returned, they are fast to parse by lua-redis which will not consume CPU. In this situation, all the CPU consumption will be on server side.

    Final words

    There are probably faster Redis clients than redis-lua:

    • https://github.com/agladysh/lua-hiredis (based on hiredis)
    • https://github.com/agladysh/ljffi-hiredis (based on hiredis, using luajit FFI)

    You may want to try them.

    0 讨论(0)
提交回复
热议问题