服务器访问redis的连接协议,Redis官方教程 Pipeline加速访问

请求/响应协议和RTT

Redis是使用客户端 - 服务器模型和所谓的请求/响应协议的TCP服务器。

这意味着通常通过以下步骤完成请求:

客户端向服务器发送查询,并通常以阻塞方式从套接字读取服务器响应。

服务器处理该命令并将响应发送回客户端。

例如,四个命令序列是这样的:

Client: INCR X

Server: 1

Client: INCR X

Server: 2

Client: INCR X

Server: 3

Client: INCR X

Server: 4

客户端和服务器通过网络链接连接。这样的链接可以非常快(环回接口)或非常慢(在因特网上建立的连接,在两个主机之间有许多跳)。无论网络延迟是什么,数据包都有时间从客户端传输到服务器,然后从服务器返回到客户端以进行回复。

此时间称为RTT(往返时间)。当客户端需要连续执行许多请求时(例如,将多个元素添加到同一列表或使用多个键填充数据库),很容易看出这会如何影响性能。例如,如果RTT时间是250毫秒(在因特网上的链路非常慢的情况下),即使服务器能够每秒处理100k个请求,我们也能够以每秒最多四个请求进行处理。

如果使用的接口是环回接口,则RTT要短得多(例如我的主机报告0,044毫秒,ping 127.0.0.1),但如果你需要连续执行多次写入,它仍然很多。

幸运的是,有一种方法可以改进这种场景。

Redis Pipelining

可以实现请求/响应服务器,以便即使客户端尚未读取旧响应,它也能够处理新请求。这样就可以将多个命令发送到服务器而无需等待回复,最后只需一步即可读取回复。

这被称为流水线技术,并且是几十年来广泛使用的技术。例如,许多POP3协议实现已经支持此功能,大大加快了从服务器下载新电子邮件的过程。

Redis从很早就开始支持流水线操作,因此无论您运行什么版本,都可以使用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

重要说明:当客户端使用流水线发送命令时,服务器将被强制使用内存对回复进行排队。因此,如果您需要使用流水线发送大量命令,最好将它们作为具有合理数量的批次发送,例如10k命令,读取回复,然后再次发送另外10k命令,依此类推。速度将几乎相同,但使用的额外内存将最大为此10k命令的回复排队所需的数量。

这不仅仅是RTT的问题

流水线技术不仅仅是为了减少由于往返时间而导致的延迟成本的一种方式,它实际上提高了您在给定Redis服务器中每秒可执行的总操作量。这是因为,在不使用流水线技术的情况下,从访问数据结构和产生回复的角度来看,为每个命令提供服务是非常便宜的,但从做插槽I /的角度来看,这是非常昂贵的。 O. 这涉及调用read()和write()系统调用,这意味着从用户域到内核域。上下文切换是一个巨大的速度惩罚。

使用流水线操作时,通常使用单个read() 系统调用读取许多命令,并通过单个write()系统调用传递多个回复。因此,每秒执行的总查询数最初会随着较长的流水线线性增加,最终达到不使用流水线技术获得的基线的10倍,如下图所示:

efaf27ddcdc4

image.png

一些代码示例

在下面的基准测试中,我们将使用支持流水线操作的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系统中提供以下数字,在loopback接口上运行,其中流水线将提供最小的改进,因为RTT已经非常低:

without pipelining 1.185238 seconds

with pipelining 0.250783 seconds

如您所见,使用流水线技术,我们将传输速度提高了五倍。

流水线VS脚本

使用Redis脚本(在Redis 2.6或更高版本中可用),可以使用执行服务器端所需的大量工作的脚本更有效地解决流水线操作的大量用例。脚本编写的一大优点是它能够以最小的延迟读取和写入数据,使读取,计算,写入等操作非常快(在此方案中,流水线操作无法帮助,因为客户端需要先读取命令的回复它可以调用write命令)。

有时,应用程序可能还希望在管道中发送EVAL或EVALSHA命令。这是完全可能的,Redis使用SCRIPT LOAD命令显式支持它(它保证可以调用EVALSHA而不会出现失败的风险)。

附录:为什么即使在环回接口上繁忙的循环也很慢?

即使有了本页所涉及的所有背景,你仍然可能想知道为什么Redis基准测试如下(伪代码),即使在loopback接口中执行,当服务器和客户端在同一台物理机器上运行时也很慢:

FOR-ONE-SECOND:

Redis.SET("foo","bar")

END

毕竟,如果Redis进程和基准测试都在同一个框中运行,那么这不仅仅是通过内存从一个地方复制到另一个地方而没有任何实际延迟和实际网络的消息吗?

原因是系统中的进程并不总是在运行,实际上是内核调度程序让进程运行,所以会发生什么,例如,允许运行基准测试,从Redis服务器读取回复(相关)到执行的最后一个命令),并写入一个新命令。该命令现在位于环回接口缓冲区中,但是为了由服务器读取,内核应该调度服务器进程(当前在系统调用中被阻止)以运行,等等。因此,实际上,由于内核调度程序的工作方式,环回接口仍然涉及类似网络的延迟。

基本上,繁忙的循环基准测试是在网络服务器中测量性能时可以做的最愚蠢的事情。明智之举就是以这种方式避免基准测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值