继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Redis pipeline

2019-03-07 20:38:527024浏览

张勤一

6实战 · 22手记 · 14推荐
TA的实战

关于 Redis pipeline

为什么需要 pipeline ?

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

实现思路

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

实现原理

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

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;

/**
 * <h1>Redis Pipeline 功能测试用例</h1>
 * 参考: 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<Object> 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<Object> sessionCallback = new SessionCallback<Object>() {
            @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 分别执行。

·······························
欢迎关注课程:

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP