目 录CONTENT

文章目录
Go

go-redis 测试 Pipeline的差异

Hello!你好!我是村望~!
2023-02-18 / 0 评论 / 6 点赞 / 209 阅读 / 1,202 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

参考链接

为什么需要 Pipeline

Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务器。

这意味着请求通常按如下步骤处理:

  • 客户端发送一个请求到服务器,并以阻塞的方式从socket读取数据,获取服务端响应 。
  • 服务端处理请求命令并发送响应回给客户端。

假如我们分次执行下面四个命令的话,它的大致处理流程如下:

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

Clients 和 Servers 通过网络连接. 可以是本地非常快的网络,或者是通过互联网连接很远的网络。不管网络延迟如何,数据包从客户端发给服务端,再从服务端返回给客户端都要花费一个时间。这个时间叫做 RTT (Round Trip Time往返时间:网络请求从起点到达目的地并再次返回起点所需的持续时间).

所以如果客户端需要连续发送多个请求的情况下,RTT对性能的影响是很严重的。例如在延迟很大的网络中RRT是250ms,即使服务端每秒能处理10万个请求,我们也只能每秒最多处理四个请求。

如果使用本机网络,这个RTT回非常小,一般小于1ms,但是如果大量请求累加起来仍然会影响性能。

一个好的办法就是使用 Pipeline

Redis Pipelining

对于基于 Request/Response 的服务器可以被实现成处理新的请求,即使客户端还没收到旧的响应。

这样就可以同时发送多个命令给服务器而不用等待响应,最后统一读回多个返回。

[当然这种只适用于命令结果之间没有依赖的情况,比如后面一个命令的执行需要前一个命令的执行结果!这样就不可能不太合适了!命令处理之间不能有依赖关系!]

这种方式被叫做 pipelining, 是一个广泛使用多年的技术,批量发送,批量返回。例如POP3协议也是这样,来加速从服务器上下载邮件。

不再每个命令都花费一次RTT,只用了一个RTT就执行了所有命令。第一个例子修改如下:

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

特别注意!

当客户端使用管道 pipelining发送命令时,服务器端需要消耗内存来存放响应

所以如果你需要发送大量的命令,最好分批发送,否则内存不够会爆掉redis的服务!

例如一次发送1万个,读取回报,再循环发剩余的命令。度上几乎无差异,但是内存最大消耗1万个 命令&回复结果 的内存。

go-redis 测试

go-redis Pipeline example

  • Redis Incr 命令将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
  • Redis Expire 命令设置 key 的过期时间(seconds)。 设置的时间过期后,key 会被自动删除。

那么我们下面就执行下面的redis命令!来测试执行时间!

1.首先设置一个key

2.把刚刚设置的值+1

3.更改key的过期时间

首先测试不使用 Pipeline 的执行时间

func TestNoPipelineLine() {
	start := time.Now()
	key := "pipeline_counter_1"
	rdb.Set(key, "100", time.Hour)
	rdb.Incr(key)
	rdb.Expire(key, time.Hour*2)
	fmt.Printf("TestNoPipelineLine 执行时间 %v", time.Since(start))
}
╰─ go run main.go                                                                                                                                                                                         ─╯
redis connect successfully!
TestNoPipelineLine 执行时间 20.647982ms

然后测试 使用 Pipeline 的执行时间

func TestPipelineLine() {
	start := time.Now()
	key := "pipeline_counter_2"
	pipeline := rdb.Pipeline()
	pipeline.Set(key, "100", time.Hour)
	pipeline.Incr(key)
	rdb.Expire(key, time.Hour*2)
	pipeline.Exec()
	fmt.Printf("TestPipelineLine 执行时间 %v", time.Since(start))
}
╰─ go run main.go                                                                                                                                                                                         ─╯
redis connect successfully!
TestPipelineLine 执行时间 8.08451ms%    

我自己把上面的代码执行了很多遍,TestNoPipelineLine 不使用 Pipeline 的情况下,时间大概都在 20-30 ms之间!使用 Pipeline 的时间基本都在 8-9 ms左右!执行的命令数量越多!差距越大!其中大部分都差异都是在 RTT 这个网络请求往返时间上面!

6

评论区