参考链接
为什么需要 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 测试
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 这个网络请求往返时间上面!
评论区