美文网首页
探究Redis 07:使用管道加速请求

探究Redis 07:使用管道加速请求

作者: wzhwizard | 来源:发表于2020-08-18 16:55 被阅读0次

请求/响应 协议与往返时延

Redis 基于TCP协议实现服务端,使用客户端服务器模型(请求/应答 协议)进行通讯。

这决定了Redis中,请求需要经过以下步骤才能完成:

  • 客户端发送请求到服务端,之后等待读取来自服务端的响应内容(通常采用阻塞模型)。
  • 服务端处理客户端命令,并将响应返回给客户端。

举个例子,连续的四条命令执行过程类似这样:

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

客户端与服务器之间通过网络连接进行通讯,这种连接可以很快(例如:本地回环)或者也可能非常慢(例如:一个跨越多个节点的互联网连接)。但无论如何,一个数据包从客户端发送到服务器并成功返回总要耗费一段时间。

这段时间称为往返时延(RTT)。当客户端需要连续执行大量请求(例如:向一个列表中加入大量元素或者从数据库中映射大量键)时,我们很容易发现这种请求响应模型对性能的影响。举个例子:在一个连接状况不好的互联网环境下,RTT为250毫秒,即便服务端每秒可以轻松处理十万请求,我们每秒钟也仅仅能处理4个请求。

如果采用本地回环连接,RTT可以很短(例如我的机器在本机ping 127.0.0.1地址只需要0.044毫秒),但是当请求量巨大时,这个短暂的延时仍然影响巨大。

幸运的是,我们有方法应对这种情况。

Redis 管道

我们可以实现这样一种请求响应模型,客户端在收到之前发送请求响应之前允许发送新的请求。这样,我们就可以实现快速发送多条命令到服务器而无需等待,之后统一处理多条响应的高效通讯场景。

这种模型被称为管道, 事实上类似的技术已被大范围应用了几十年。例如:我们熟知的POP3协议就已经支持这个特性,可以实现动态加速新邮件下载速度。

Redis 从一开始就支持了这个特性,因此不管你在用哪个Redis版本,都可以使用管道技术。
以下是一个采用了netcat utility的示例:

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

扎这种模型下我们无需对每次请求耗费RTT时间,而是三条命令仅需一次RTT。

之前的示例如果使用了管道之后,其执行命令顺序如下:

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

重要提示: 由于在管道通讯模型下,服务端会在请求发送时,强制缓存全部响应内容到内存中。因此,当需要发送大量请求时,最好分批次进行发送。例如:每10k个命令发送后,读取一次响应,之后再发送另外10k请求,以此类推。这种分批处理方式不会影响总体处理效率,但可以保证服务端缓存的响应内容不至于太多。

除了RTT还有其他好处

管道技术其实不仅减少了RTT,事实上,在Redis server中,采用管道技术也大大减少了需要处理的命令数量。
在不采用管道技术的情况下,但从操作数据并返回响应内容角度看,影响并不大,但是对于 socket I/O来说,每条命令都会产生独立的read()write() 系统调用, 伴随从用户态到内核态的巨大切换开销。

在使用管道之后,多条命令通过一个read()系统调用完成读取, 并且多个响应结果也仅需一次write()系统调用便可发送成功。采用管道后,每秒处理请求数量随着管道长度近乎线性增长,最终接近10倍与常规通讯模型的效率,如下图:

Pipeline size and IOPs

真实代码案例

在下面的评测中我们用了支持管道技术的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倍。

管道还是脚本

使用Redis scripting (Redis2.6之后支持) 功能可以更高效的完成一些管道适用的用户场景,但在服务端需要更复杂的编程工作。
脚本的好处是可以支持延时更低的读写数据操作,使得 read, compute, write 操作更快, (在写入前需要读取操作的情况下,管道无法提供更多效率提升)。

相关文章

  • 探究Redis 07:使用管道加速请求

    请求/响应 协议与往返时延 Redis 基于TCP协议实现服务端,使用客户端服务器模型(请求/应答 协议)进行通讯...

  • Redis管道技术的使用

    目录 Redis 管道技术 SpringDataRedis 使用管道 Redis 管道的性能测试 使用管道技术的注...

  • Redis 管道技术

    Redis 管道技术作为了解,暂时没有实际使用 Redis正常请求情况 Redis是一种基于客户端-服务端模型以及...

  • Redis-管道

    Redis管道的使用原文介绍:http://redis.cn/topics/pipelining.html[htt...

  • PHP中利用redis实现消息队列处理高并发请求

    将请求存入redis 为了模拟多个用户的请求,使用一个for循环替代 //redis数据入队操作 $redis =...

  • redis基准测试

    使用redis自带得redis-benchmark。 -n 请求总数。-t 请求类型-r key的随机范围-c ...

  • pipeline、mget、transaction区别

    pipeline 管道 一般情况下,Redis Client端发出一个请求后,通常会阻塞并等待Redis服务端处理...

  • Redis 管道技术

    Redis 管道技术 Redis是一种基于客户端-服务端模型(C/S模型)以及请求/响应协议的TCP服务。 这意味...

  • 07-Redis 管道

    Redis 管道 redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如...

  • Redis管道

    Redis之管道的使用原文地址: https://blog.piaoruiqing.com/blog/2019/...

网友评论

      本文标题:探究Redis 07:使用管道加速请求

      本文链接:https://www.haomeiwen.com/subject/pmkkjktx.html