共计 2622 个字符,预计需要花费 7 分钟才能阅读完成。
更多技术文章,请关注我的集体博客 www.immaxfang.com 和小公众号
Max 的学习札记
。
Redis 客户端和服务端之间是采纳 TCP 协定进行通信的,是基于 Request/Response 这种一问一答的模式,即申请一次响应一次。
一般模式
咱们先来看下一般模式下,一条 Redis 命令的简要执行过程:
- 客户端发送一条命令给 redis-server,阻塞期待 redis-server 应答
- redis-server 接管到命令,执行命令
- redis-server 将后果返回给客户端
上面咱们来简要理解下一个残缺申请的交互过程。
- 客户端调用 write() 将音讯写入操作系统为 socket 调配的 send buffer 中
- 操作系统将 send buffer 中的内容发送到网卡,网卡通过网关路由把内容发送到服务器网卡
- 服务器网卡将承受到的音讯写入操作系统为 socket 调配的 recv buffer
- 服务器过程调用 read() 从 recv buffer 中读取音讯进行解决
- 解决实现之后,服务器调用 write() 将响应内容发送的 send buffer 中
- 服务器将 send buffer 中的内容通过网卡,发送到客户端
- 客户端操作系统将网卡中的内容放入 recv buffer 中
-
客户端过程调用 read() 从 recv buffer 中读取音讯
一般模式的问题
咱们来想一下,这种状况下可能导致什么问题。
如果同时执行大量的命令,那对于每一个命令,都要按下面的流程走一次,以后的命令须要期待上一条命令执行应答结束之后,才会执行。这个过程中会有屡次的 RTT,也还会随同着很多的 IO 开销,发送网络申请等。每条命令的发送和接管的过程都会占用两边的网络传输。
简略的来说,每个命令的执行工夫 = 客户端发送耗时 + 服务器解决耗时 + 服务器返回耗时 + 一个网络来回耗时。
在这里,一个 网络来回耗时(RTT)是不好管制的,也是不稳固的。它的影响因素很多,比方客户端到服务器的网络线路是否拥挤,通过了多少跳。还有就是 IO 零碎调用也是耗时的,一个 read 零碎调用,须要从用户态,切换到内核态。上文咱们讲述一个命令的申请过程时屡次降到 read 和 write 零碎调用。
能够说一个命令的执行工夫,很大水平上受到它们的限度。pipeline 模式
有没有什么办法来解决这种问题呢。
第一种办法,就是利用多线程机制,并行执行命令。
第二种办法,调用批量命令,例如mget
等,一次操作多个键。
很多时候咱们要执行的命令并不是一样的命令,而是一组命令,这个时候就无奈应用相似mget
这样的批量命令了。那还有其余的办法吗?
回忆一下,咱们初学编程的时候,新手都会通知咱们,不要在循环外面做查问。我有一个 books 列表数据,要依据 book_id 查问它们的 price,如果咱们循环 books 列表,在每次循环外面取查问单个 book_id 的 price,那性能必定是不现实的。个别咱们的优化形式是将多个 book_id 取出来,一次性去查多个 book_id 的 price,这样性能就有显著的提醒。行将屡次小命令中的耗时操作合并到一次,从而缩小总的执行工夫。
相似的,Redis pipeline 呈现了,个别称之为管道。它容许客户端一次能够发送多条命令,而不必像一般模式那样每次执行一个小命令都要期待前一个小命令执行完,服务器在接管到一堆命令后,会顺次执行,而后把后果打包,再一次性返回给客户端。
这样能够防止频繁的命令发送,缩小 RTT,缩小 IO 调用次数 。后面曾经介绍了,IO 调用会波及到用户态和内核态之间的切换,在高性能的一些零碎中,咱们都是尽可能的缩小 IO 调用。
简要流程如下图:
-
pipeline 的长处
- 缩小 RTT
- 缩小 IO 调用次数
-
根本应用
Pipeline pipeline =jedis.pipelined(); for(int i = 0; i < 100; i++){pipeline.rpush("rediskey", i + ""); } pipeline.sync()
总结一下 pipeline 的外围,就是
客户端将一组 Redis 命令进行组装,通过一次 RTT 发送给服务器,同时服务器再将这组命令的执行后果依照程序一次返回给客户端
。pipeline 留神问题
尽管 pipeline 在某些状况下会带来不小的性能晋升,然而,咱们在应用的时候也须要留神。
- pipeline 中的命令数量不宜过多。
客户端会先将多个命令写入内存 buffer 中(打包),命令过多,如果是超过了客户端设置的 buffer 下限,被客户端的解决策略解决了(不同的客户端实现可能会有差别,比方 jedis pipeline,限度每次最大的发送字节数为 8192,缓冲区满了就发送,而后再写缓冲,最初才解决 Redis 服务器的应答)。如果客户端没有设置 buffer 下限或不反对下限设置,则会占用更多的客户端机器内存,造成客户端瘫痪。官网举荐是每次 10k 个命令。
倡议做好标准,遇到一次蕴含大量命令的 pipeline,能够拆分成多个稍小的 pipeline 来实现。
- pipeline 一次只能运行在一个 Redis 节点上,一些集群或者 twemproxy 等中间件应用须要留神。
在集群环境下,一次 pipeline 批量执行多个命令,每个命令须要依据 key 计算槽位,而后依据槽位去特定的节点下来执行命令,这样一次 pipeline 就会应用多个节点的 redis 连贯,这种以后也是不反对的。
- pipeline 不保障原子性,如要求原子性,不倡议应用 pipeline
它仅是将多个命令打包发送进来而已,如果两头有命令执行异样,也会继续执行残余命令。
pipeline 与批量操作 mget 等区别
其实 meget
和 pipeline 优化的方向是统一的,即多个命令打包一次发送,缩小网络工夫。然而也是有区别的。
mget
等的场景是一个命令对应多个键值对,而 pipeline 个别是多条命令(不同的命令)mget
操作是一个原子操作,而 pipeline 不是原子操作-
mget
是服务端实现,而 pipeline 是客户端和服务端独特实现pipeline 与事务的区别
这两者关注和解决的问题不是一个货色,原理也不一样。
- pipeline 是一次申请,服务端程序执行,一次返回。而事务是屡次申请(先 multi,再多个操作命令,最初 exec),服务端程序执行,一次返回
-
pipeline 关注的是 RTT 工夫和 IO 调用,事务关注的是一致性问题
总结
本文次要讲了多命令执行时耗时问题,以及 pipeline 的解决办法,和其简略的原理,以及留神点。明天的学习就到这里,改天咱们接着肝。