流水作业:学习如何一次发送多个命令,省去往返时间

翻译参考:Pipelining:Learn how to send multiple commands at once, saving on round trip time.

请求/相应协议和RTT

Redis是基于TCP的C/S模型服务器,TCP是请求/响应协议。这意味着一个请求的完成要有以下几步:

  • 客户端向服务器发送查询请求,或客户端从socket读取服务器的响应(socket通常是阻塞的)。
  • 服务器处理客户端的请求命令,将响应发给客户端。

例如有四个命令如下:

Client: INCR X
Server: 1
Client: INCR X
Server: 2
Client: INCR X
Server: 3
Client: INCR X
Server: 4

客户端和服务器通过网络连接。这个连接可能很快,例如一个回环,或者很慢,例如这个连接经过很多跳。不论怎么,网络延时都有,因为一个数据包从客户端发送到服务端,再从服务端发回给客户端需要时间。
这个时间叫做RTT(Round Trip Time)。当客户端需要连续执行多个命令(例如向同一个链表添加许多元素,或从一个数据库中取很多键值),很容易看出这将如何影响其性能。例如当RTT为250ms时,即使Redis服务器处理能力为100k/s,它也只能每秒处理四个请求。
如果使用的回环网络,RTT事件会小很多(我的电脑ping 127.0.0.1为0.044ms),但是如果你依次执行多个命令时,还是有很大的往返时间。
幸运的是,针对这种情况,可以有方法提升其性能。

Redis流程线作业

请求响应服务器可以这样来实现,服务器在客户端没有收到响应时可以处理新的请求。这样的话,客户端可以一次性向服务器发送多个请求而不必等待响应,最后只需一步读取所有响应。
这叫做流程线作业(pipelining),这项技术已经广泛使用了几十年。例如,许多POP3协议的实现已经支持这个特性,极大的加速了从服务器下载新邮件的速度。
Redis最开始就支持流水线作业,因此无论你使用那个版本,你都可以使用这个特性。这是一个使用netcat的例子:

$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
+PONG
+PONG
+PONG

在上面的例子中,不必为每一个命令耗费RTT,而是一次性发送了三个命令。为了显示对比,上面的第一个例子使用流水线作业将是:

Client: INCR X
Client: INCR X
Client: INCR X
Client: INCR X
Server: 1
Server: 2
Server: 3
Server: 4

注意 :当客户端使用流水作业发送命令时,服务器将使用queue来存储响应,这将占用内存。因此如果你需要一次发送大量命令,最好把命令控制在合理数量,例如10k。发送10k,读取响应,再发送10k。速度几乎形同,但是额外使用最大内存为queue存储这10k命令的响应。

一个基准测试

在下面的基准测试中,我们使用Redis的Ruby客户端,它支持流水线作业。测试使用流水线作业技术带来速度上的性能提升:

require 'rubygems'
require 'redis'

def bench(descr)
    start = Time.now
    yield
    puts "#{descr} #{Time.now-start} seconds"
end

def without_pipelining
    r = Redis.new
    10000.times {
        r.ping
    }
end

def with_pipelining
    r = Redis.new
    r.pipelined {
        10000.times {
            r.ping
        }
    }
end

bench("without pipelining") {
    without_pipelining
}
bench("with pipelining") {
    with_pipelining
}

在我的Mac OS X系统上运行上面的脚本,通过回环来访问服务器,这样的环境下流水线作业提升的性能最小,因为实际的RTT已经很小了:

without pipelining 1.185238 seconds
with pipelining 0.250783 seconds

可以看出,使用流水线作业技术,在传输性能上提升了5倍。

流水线作业vs脚本

Redis2.6及其以后的版本支持Redis scripting,在流水线作业的例子中使用Redis scripting,比在服务器端使用scripts处理工作更加高效。使用脚本的一大优势为在读数据、写数据时有比较小德延时(latency),使读、写、计算等操作非常快(在这种场景下流水线作业不能起作用,因为客户端要读取响应才能写新的命令)。
有时应用需要使用流水线技术来发送EVALEVALSHA命令。Redis显式支持,可是使用命令SCRIP LOAD(它确保了调用EVALSHA不会失败)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值