关于 Redis pipeline

  为什么需要 pipeline ?

  Redis 的工作过程是基于 请求/响应 模式的。正常情况下,客户端发送一个命令,等待 Redis 应答;Redis 接收到命令,处理后应答。请求发出到响应的时间叫做往返时间,即 RTT(Round Time Trip)。在这种情况下,如果需要执行大量的命令,就需要等待上一条命令应答后再执行。这中间不仅仅多了许多次 RTT,而且还频繁的调用系统 IO,发送网络请求。为了提升效率,pipeline 出现了,它允许客户端可以一次发送多条命令,而不等待上一条命令执行的结果。

  实现思路

  客户端首先将执行的命令写入到缓冲区中,最后再一次性发送 Redis。但是有一种情况就是,缓冲区的大小是有限制的:如果命令数据太大,可能会有多次发送的过程,但是仍不会处理 Redis 的应答。

  实现原理

  要支持 pipeline,既要服务端的支持,也要客户端支持。对于服务端来说,所需要的是能够处理一个客户端通过同一个 TCP 连接发来的多个命令。可以理解为,这里将多个命令切分,和处理单个命令一样。对于客户端,则是要将多个命令缓存起来,缓冲区满了就发送,然后再写缓冲,最后才处理 Redis 的应答。

  Redis pipeline 的参考资料

  Redis 官网对于 pipeline 的介绍

  Spring Data Redis 对于 pipeline 的介绍

  在 SpringBoot 中使用 Redis pipeline

  基于 SpringBoot 的自动配置,在使用 Redis 时只需要在 pom 文件中给出 spring-boot-starter-data-redis 依赖,就可以直接使用 Spring Data Redis。关于 Redis pipeline 的使用方法,可以阅读 Spring Data Redis 给出的解释。下面,我给出一个简单的例子:

import com.imooc.ad.Application;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.dao.DataAccessException;
  import org.springframework.data.redis.connection.StringRedisConnection;
  import org.springframework.data.redis.core.RedisCallback;
  import org.springframework.data.redis.core.RedisOperations;
  import org.springframework.data.redis.core.SessionCallback;
  import org.springframework.data.redis.core.StringRedisTemplate;
  import org.springframework.test.context.junit4.SpringRunner;
  /**
  *

Redis Pipeline 功能测试用例

* 参考: https://docs.spring.io/spring-data/redis/docs/1.8.1.RELEASE/reference/html/#redis:template
  * Created by Qinyi.
  */
  @RunWith(SpringRunner.class)
  @SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
  public class RedisPipelineTest {
  /** 注入 StringRedisTemplate, 使用默认配置 */
  @Autowired
  private StringRedisTemplate stringRedisTemplate;
  @Test
  @SuppressWarnings(all)
  public void testExecutePipelined() {
  // 使用 RedisCallback 把命令放在 pipeline 中
  RedisCallback redisCallback = connection - {
  StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
  for (int i = 0; i != 10; ++i) {
  stringRedisConn.set(String.valueOf(i), String.valueOf(i));
  }
  return null; // 这里必须要返回 null
  };
  // 使用 SessionCallback 把命令放在 pipeline
  SessionCallback sessionCallback = new SessionCallback() {
  @Override
  public Object execute(RedisOperations operations) throws DataAccessException {
  operations.opsForValue().set(name, qinyi);
  operations.opsForValue().set(gender, male);
  operations.opsForValue().set(age, 19);
  return null;
  }
  };
  System.out.println(stringRedisTemplate.executePipelined(redisCallback));
  System.out.println(stringRedisTemplate.executePipelined(sessionCallback));
  }
  }

  总结:Redis pipeline 的特性以及使用时需要注意的地方

  pipeline 减少了 RTT,也减少了IO调用次数(IO 调用涉及到用户态到内核态之间的切换)

  如果某一次需要执行大量的命令,不能放到一个 pipeline 中执行。数据量过多,网络传输延迟会增加,且会消耗 Redis 大量的内存。应该将大量的命令切分为多个 pipeline 分别执行。