美文网首页
Redis(九):Pipeline(管道)VS Lua(脚本)

Redis(九):Pipeline(管道)VS Lua(脚本)

作者: 雪飘千里 | 来源:发表于2022-10-23 15:18 被阅读0次

    1、 背景

    Redis是建立在TCP协议基础上的CS架构,客户端client对redis server采取请求响应的方式交互。
    Redis 使用的是客户端-服务器(CS)模型和请求/响应协议的 TCP 服务器。这意味着通常情况下一个请求会遵循以下步骤:

    客户端发送请求,获取socket,阻塞等待返回;
    服务端执行命令并将结果返回给客户端;

    image.png

    而当执行的命令较多时,这样的一来一回的网络传输所消耗的时间被称为RTT(Round Trip Time),显而易见,如果可以将这些命令作为一个请求一次性发送给服务端,并一次性将结果返回客户端,会节约很多网络传输的消耗,可以大大提升响应时间。

    2、管道(pipeline)

    可以一次性发送多条命令并在执行完后一次性将结果返回,pipeline 通过减少客户端与 redis 的通信次数来实现降低往返延时时间,而且 Pipeline 实现的原理是队列,而队列的原理是时先进先出,这样就保证数据的顺序性。

    image.png

    通俗点:pipeline就是把一组命令进行打包,然后一次性通过网络发送到Redis。同时将执行的结果批量的返回回来

    3、适用场景

    有些系统可能对可靠性要求很高,每次操作都需要立马知道这次操作是否成功,是否数据已经写进 redis 了,那这种场景就不适合。

    还有的系统,可能是批量的将数据写入 redis,允许一定比例的写入失败,那么这种场景就可以使用了,比如10000条一下进入 redis,可能失败了2条无所谓,后期有补偿机制就行了,比如短信群发这种场景,如果一下群发10000条,按照第一种模式去实现,那这个请求过来,要很久才能给客户端响应,这个延迟就太长了,如果客户端请求设置了超时时间5秒,那肯定就抛出异常了,而且本身群发短信要求实时性也没那么高,这时候用 pipeline 最好了。

    管道可以提升我们程序中的响应时间,同时我们不能完全依赖于它的"事务"机制,只需要把管道当批处理工具即可,在某些场合下,更需要结合管道和lua脚本一起使用。

    4、使用

    /*
         * 测试普通模式与 PipeLine 模式的效率: 
         * 测试方法:向 redis 中插入 10000 组数据
         */
        public static void testPipeLineAndNormal(Jedis jedis)
                throws InterruptedException {
            Logger logger = Logger.getLogger("javasoft");
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                jedis.set(String.valueOf(i), String.valueOf(i));
            }
            long end = System.currentTimeMillis();
            logger.info("the jedis total time is:" + (end - start));
    
            Pipeline pipe = jedis.pipelined(); // 先创建一个 pipeline 的链接对象
            long start_pipe = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                pipe.set(String.valueOf(i), String.valueOf(i));
            }
            pipe.sync(); // 获取所有的 response
            long end_pipe = System.currentTimeMillis();
            logger.info("the pipe total time is:" + (end_pipe - start_pipe));
            
            BlockingQueue<String> logQueue = new LinkedBlockingQueue<String>();
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                logQueue.put("i=" + i);
            }
            long stop = System.currentTimeMillis();
            logger.info("the BlockingQueue total time is:" + (stop - begin));
        }
    
    
    image.png

    从上述代码以及结果中可以明显的看到 PipeLine 在 “批量处理” 时的优势。

    5、 Lua脚本

    Lua脚本会将多个命令和操作当成一个命令在redis中执行,也就是说该脚本在执行的过程中,不会被任何其他脚本或命令打断干扰。正是因此这种原子性,lua脚本才可以代替multi和exec的事务功能。同时也是因此,在lua脚本中不宜进行过大的开销操作,避免影响后续的其他请求的正常执行。

    • 使用lua脚本的好处
      lua脚本是作为一个整体执行的,所以中间不会被其他命令插入;
      可以把多条命令一次性打包,所以可以有效减少网络开销;
      lua脚本可以常驻在redis内存中,所以在使用的时候,可以直接拿来复用,也减少了代码量;
    • 应用
      我们可以在redis里使用eval命令调用lua脚本,且该脚本在redis里作为单条命令去执行不会受到其余命令的影响,非常适用于高并发场景下的事务处理。同样我们可以在lua脚本里实现任何想要实现的功能,迭代,循环,判断,赋值 都是可以的。

    6、 Pipeline(管道)VS Lua(脚本)

    • 原子性
      脚本会将多个命令和操作当成一个命令在redis中执行,也就是说该脚本在执行的过程中,不会被任何其他脚本或命令打断干扰,具有原子性,在执行脚本的时候不会被其他的命令插入,因此更适合于处理事务;
      而管道虽然也会将多个命令一次性传输到服务端,但在服务端执行的时候仍然是多个命令,如在执行CMD1的时候,外部另一个客户端提交了CMD9,会先执行完CMD9再执行管道中的CMD2,因此事实上管道是不具有原子性的

    • 使用场景
      就场景上来说,正因为Lua脚本会被视为一个命令去执行,因为Redis是单线程执行命令的,所以我们不能在lua脚本里写过于复杂的逻辑,否则会造成阻塞,因此lua脚本适合于相对简单的事务场景;
      而管道因为不具有原子性,因此管道不适合处理事务,但管道可以减少多个命令执行时的网络消耗,可以提高程序的响应速度,因此管道更适合于管道中的命令互相没有关系,不需要有事务的原子性,且需要提高程序响应速度的场景。

    相关文章

      网友评论

          本文标题:Redis(九):Pipeline(管道)VS Lua(脚本)

          本文链接:https://www.haomeiwen.com/subject/nwkeortx.html