文章目录加载中

Redis 管道

# 什么是管道(Pipeline)?

通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与 Redis 的通信次数来实现降低往返时延累计值的目的。

不使用管道时的命令执行示意图(纵向表示时间)(如上图)

使用管道时的命令执行示意图

# 深入理解:为什么 pipeline 会提速?

一个完整的交互流程如下:

  1. 客户端进程调用 write()把消息写入到操作系统内核为 Socket 分配的 send buffer 中
  2. 操作系统会把 send buffer 中的内容写入网卡,网卡再通过网关路由把内容发送到服务器端的网卡
  3. 服务端网卡会把接收到的消息写入操作系统为 Socket 分配的 recv buffer
  4. 服务器进程调用 read()读取消息然后进行处理
  5. 处理完成后调用 write()把返回结果写入到服务器端的 send buffer
  6. 服务器操作系统再将 send buffer 中的内容写入网卡,然后发送到客户端
  7. 客户端操作系统将网卡内容读到 recv buffer 中
  8. 客户端进程调用 read()从 recv buffer 中读取消息并返回

这其中除了网络开销,花费时间最长的就是进行系统调用 write()和 read()了,这一过程需要操作系统由用户态切换到内核态,中间涉及到的上下文切换会浪费很多时间

使用管道时,多个命令只会进行一次 read()和 wrtie()系统调用,因此使用管道会提升 Redis 服务器处理命令的速度,随着管道中命令的增多,服务器每秒处理请求的数量会线性增长,最后会趋近于不使用管道的 10 倍。

# 代码使用

About pipeline():The commands are queued in memory and flushed to Redis by calling the exec method

关于 pipeline() 方法:这个方法将命令放入内存队列中,当调用exec时,他们会传给 redis 并且执行。

const Redis = require("ioredis");
const redis = new Redis();

// 链式调用
redis
  .pipeline()
  .set("foo", "bar")
  .del("cc")
  .exec((err, results) => {});

# 注意事项

在 Redis 中,如果客户端使用管道发送了多条命令,那么服务器就会将多条命令放入一个队列中(这里会消耗内存进行存储)。

所以管道中命令的数量并不是越大越好(太大容易撑爆内存),而是应该有一个合理的值。

# 参考链接

本文来自心谭博客:xin-tan.com,经常更新web和算法的文章笔记,前往github查看目录归纳:github.com/dongyuanxin/blog