Redis之(pipline)管道

Redis的Pipeline技术能批量发送命令,减少网络IO,提高效率。它不是原子操作,适用于批量写入数据且允许部分失败的场景。与连接池结合使用,能显著提升性能。相比事务,管道更灵活,但在需要原子性时推荐使用脚本。Redis 2.6以后,脚本在大多数场景下优于管道。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,Redis管道概念

  1. redis-py默认执行每次连接都会创建和断开一次连接操作,如果一次请求中想要执行多个命令需要使用pipline
    import redis
    
    conn = redis.StrictRedis(host='127.0.0.1',port=6379)
    #创建管道
    pip = conn.pipeline(transaction=False)
    #缓冲多个命令
    pip.keys('*').set('name','Max').sadd('sex','boy').incr('num')
    pip.execute()#返回一个列表
    #也可组装一起写
    # pip.keys('*').set('name','Max').sadd('sex','boy').incr('num').execute()
    
  2. 并且pipline默认是原子操作
    #python可以使用管道来代替事务
    try:
        pipe.multi()
        pipe.set('age1',22)
        pipe.set('age2',23)
        pipe.set('age3',24)
        time.sleep(5)
        pipe.execute()
        print(r.mget('age1','age2','age3'))
    except redis.exceptions.WatchError as e:
        print('Error')
    
    • redis中multi和pipline区别
      • redis.multi()方法会将命令逐条发给redis服务端。只有在需要使用事物时才选择redis.multi()I方法,它可以保证发给redis的一系列命令以原子方式执行。但效率相应也是最低的
      • redis.pipeline()方法,pipeline 只是把多个redis指令一起发出去,redis并没有保证这些指定的执行是原子的。如果只是为了一下执行多条redis命令,无需事物和原子性,那么应该选用redis.pipeline()方法。代码的性能会有大幅度提升!
      • pipeline方式执行效率要比其他方式高10倍左右的速度,启用multi写入要比没有开启慢一点。
  3. 管道使redis的读写速度更加的快速。秒级取值1000+的数据
  4. 需要注意到是:
    • 用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并是不是打包的命令越多越好。具体多少合适需要根据具体情况测试。
  5. redis管道适用场景
    • 批量将数据写入redis,允许一定比例的写入失败
    • 比如10万条一下进入redis,可能失败了几条无所谓,后期有补偿机制就行了,比如短信群发这种场景,如果一下群发10万条,按照第一种模式去实现,那这个请求过来,要很久才能给客户端响应,这个延迟就太长了,如果客户端请求设置了超时时间5秒,那肯定就抛出异常了,而且本身群发短信要求实时性也没那么高,这时候用pipeline最好了。
    • pipeline 底层是通过把所有的操作封装成流,redis有定义自己的出入输出流。在 sync() 方法执行操作,每次请求放在队列里面,解析响应包。

二,管道与连接池

  1. 使用pipeline(管道),通过缓冲多条命令,然后一次性执行的方法减少服务器–客户端之间TCP数据库包,从而提高效率

    import redis
    
    # conn = redis.StrictRedis(host='127.0.0.1',port=6379)
    pool = redis.ConnectionPool(host='192.168.22.147',port=6379)
    conn = redis.StrictRedis(connection_pool=pool)
    #创建管道
    pip = conn.pipeline(transaction=False)
    
    • 利用pipeline取值3500条数据,大约需要900ms,如果配合线程or协程来使用,每秒返回1W数据是没有问题的,基本能满足大部分业务。

三,管道的优缺点

  1. 优点:
    • 可以使用各种redis命令,使用更灵活
    • 管道可以将多条命令打包一起发送,并且在一次回复中接收所有被执行命令的结果,使用这个功能可以有效地提升程序在执行多条命令时的效率
    • 服务器端和客户端各一次read()write()系统调用,减少客户端与服务器之间的通信次数,节约时间
  2. 缺点:
    • redis必须在处理完所有命令前先缓存起所有命令的处理结果会占用内存
    • 打包的命令越多,缓存消耗内存也越多。最好以具有合理数量的批处理方式发送它们,例如10k命令,阅读答复,然后再次发送10k命令,依此类推。
      • 因为管道通常是通过单个read() 系统调用读取许多命令,而通过单个系统调用传递多个答复write()。因此,每秒执行的总查询数最初会随着管道较长而线性增加,最后会趋近于不使用管道的10倍。
    • Redis2.6版本以后,脚本在大部分场景中的表现要优于管道。

四,拓展

  1. 客户端和服务端都在同一物理机上,为什么还是很慢?
    • 前面我们提到,为了解决网络开销带来的延迟问题,可以把客户端和服务器放到一台物理机上。但是有时用benchmark进行压测的时候发现这仍然很慢。
    • 这时客户端和服务端实际是在一台物理机上的,所有的操作都在内存中进行,没有网络延迟,按理来说这样的操作应该是非常快的。为什么会出现上面的情况的呢?
      • 实际上,这是由内核调度导致的。比如说,benchmark运行时,读取了服务器返回的结果,然后写了一个新的命令。这个命令就在回环接口的send buffer中了,如果要执行这个命令,内核需要唤醒Redis服务器进程。所以在某些情况下,本地接口也会出现类似于网络延迟的延迟。其实是内核特别繁忙,一直没有调度到Redis服务器进程。
  2. 深入理解Redis交互流程
    • 图片解释
    • 文字解释一个完整的交互流程如下:
      • 客户端进程调用write()把消息写入到操作系统内核为Socket分配的send buffer中
      • 操作系统会把send buffer中的内容写入网卡,网卡再通过网关路由把内容发送到服务器端的网卡
      • 服务端网卡会把接收到的消息写入操作系统为Socket分配的recv buffer
      • 服务器进程调用read()读取消息然后进行处理
      • 处理完成后调用write()把返回结果写入到服务器端的send buffer
      • 服务器操作系统再将send buffer中的内容写入网卡,然后发送到客户端
      • 客户端操作系统将网卡内容读到recv buffer中
      • 客户端进程调用read()从recv buffer中读取消息并返回
    • 命令执行的时间进一步细分:
      • 命令的执行时间 = 客户端调用write并写网卡时间+一次网络开销的时间+服务读网卡并调用read时间++服务器处理数据时间+服务端调用write并写网卡时间+客户端读网卡并调用read时间
      • 这其中除了网络开销,花费时间最长的就是进行系统调用write()read()了,这一过程需要操作系统由用户态切换到内核态,中间涉及到的上下文切换会浪费很多时间。
      • 使用管道时,多个命令只会进行一次read()wrtie()系统调用,因此使用管道会提升Redis服务器处理命令的速度

五,区别

  1. 事务、管道和脚本的区别
    • 事务和脚本(在Redis 2.6或更高版本中可用)都能满足原子性的要求,其区别在于脚本可借助Lua语言可在服务器端存储以最小的延迟读写数据,但脚本无法处理长耗时的操作
    • redis确保这条脚本**脚本执行期间,其它任何脚本或者命令都无法执行。**正是由于这种原子性,脚本才可以替代MULTI/EXEC作为事务使用。
    • 应用程序可能还希望在管道中发送EVALEVALSHA命令,Redis通过SCRIPT LOAD命令明确支持它(它保证可以调用EVALSHA而不会失败)
    • 官方文档也说了,正是由于脚本执行的原子性,所以我们不要在脚本中执行过长开销的程序,否则会验证影响其它请求的执行。
    • 管道是无状态操作集合,使用管道可能在效率上比使用脚本要好,但是有的情况下只能使用脚本
    • 因为在执行后面的命令时,无法得到前面命令的结果,就像事务一样,所以如果需要在后面命令中使用前面命令的value等结果,则只能使用脚本或者事务+watch
  2. 使用mset,管道,管道中加事务与事务发送命令的关系:
    • mset和管道两者发送机制不同使用mset发送命令是发送一次返回一次结果(如发送四次返回四次即产生八次报文)但是管道是将所有命令一次性全部发送然后全部返回。(发送命令越多占的内存越多)管道节省资源。
    • 如果for 循环 次数越多,其实,mset和mget更快
    • mset性能最好,吞吐量最高,因为mset是作为单条命令执行,在命令解析和执行上都更有效率
    • pipeline好于transaction in pipeline,因为事务会导致命令入列和出列会稍许浪费cpu时间
    • transaction in pipeline微弱领先于transaction,但是几乎没有区别,可以理解为pipeline在命令传输上更有效率。
    • 总得来说,在批量模式下,四种操作都比普通的get/set性能上有几大的提升。
    • 在当前生产环境中使用较多的Redis Cluster环境中,上述四种批量操作的使用场景都比较有限,其中transaction不支持,pipeline建议仅用于单slot且目前支持的客户端很少,mget/mset也仅仅可以操作于单slot中的key。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值