我的目标是让我们的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-lua或webdis)时,最大CPU使用率低于60%。
我浏览了webdis
和redis-lua
中的一些代码。 webdis
取决于hiredis
,redis-lua
在Lua中实现,并依赖于套接字(lua-socket
)。
与Redis基准测试相比,这些客户端是否太慢,无法最大化Redis CPU消耗?
我还浏览了redis-benchmark.c
中的一些代码。基准测试的主要工作在aeMain
完成。似乎redis-benchmark
使用Redis的快速代码,而我的测试客户端(webdis
和redis-lua
)则没有。
目前我的客户有两种选择:
redis-lua
webdis
然而,这两个并没有最大化Redis的CPU利用率(低于60%)。还有其他选择吗?
或者,是否可以在redis-benchmark
工具本身之外充分利用redis-server?
答案 0 :(得分:7)
我怀疑最大化Redis的CPU使用率将有利于您的后端设计。正确的问题是Redis是否足够有效以在给定的延迟时间内维持吞吐量。 Redis是一个单线程服务器:在80%的CPU消耗下,延迟可能非常糟糕。
我建议您测量延迟,而redis-benchmark在尝试增加Redis CPU消耗之前,看看它是否可以满足您的需求。 redis-cli的--latency选项可用于此:
现在,如果你真的想增加Redis CPU消耗,你需要一个高效的客户端程序(比如redis-benchmark),能够同时处理多个连接,或者你的客户端程序的多个实例。
Lua是一种快速解释的语言,但它仍然是一种解释语言。它比C代码慢一到两个数量级。 Redis在解析/生成协议方面要比lua-redis快得多,因此您将无法使用独特的Lua客户端使Redis饱和(除非您使用O(n)Redis命令 - 请参阅后面的内容。)
webdis在C中实现,具有高效的客户端库,但必须解析比Redis协议更加冗长和复杂的http / json协议。对于大多数操作,它可能比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程序占Redis CPU的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客户端可能比redis-lua更快:
您可能想尝试一下。