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

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

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 操作更快, (在写入前需要读取操作的情况下,管道无法提供更多效率提升)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343