我的目标是让我们的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中实现,并依赖于socket(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?
我怀疑最大化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更快:
你可能想尝试一下。