这次的内容是我自己为了总结Redis知识而扩充的,上一篇其实已经总结了几点知识了,但是Redis的强大,以及适用范围之广可不是单单一篇博文就能总结清的。所以这次准备继续总结,因为第一个问题,Redis的批量操作,是我在面试过程中被真实问到的,当时没答上来,也是因为确实没了解过Redis的批量操作。
当时的问题,我还记得比较清晰:Redis执行批量操作的功能是什么?使用场景就是搞促销活动时,会做预缓存,会往缓存里放大批数据,如果直接放的话那么会很慢,怎么能提高效率呢?
Redis的批量操作-管道(pipeline)首先Redis的管道(pipeline)并不是Redis服务端提供的功能,而是Redis客户端为了减少网络交互而提供的一种功能。
正常的一次Redis网络交互如下:
pipeline主要就是将多个请求合并,进行一次提交给Redis服务器,Redis服务器将所有请求处理完成之后,再一次性返回给客户端。
下面我们分析一下pipeline的原理
pipeline的一个交互过程是这样的:
客户端进程调用write命令将消息写入到操作系统内核为套接字分配的发送缓冲区send buffer。
客户端操作系统通过网络路由,将send buffer中的数据发送给服务器操作系统为套接字分配的接收缓冲区 receive buffer。
服务端进程调用read命令从receive buffer中取出数据进行处理,然后调用write命令将相应信息写入到服务端的send buffer中。
服务端操作系统通过网络路由,将send buffer中的数据发送给客户端操作系统的receive buffer。
客户端进程调用read命令将数据从receive buffer中取出进行业务处理。
在使用pipeline时需要注意:
pipeline执行的操作,和mget,mset,hmget这样的操作不同,pipeline的操作是不具备原子性的。
还有在集群模式下因为数据是被分散在不同的slot里面的,因此在进行批量操作的时候,不能保证操作的数据都在同一台服务器的slot上,所以集群模式下是禁止执行像mget、mset、pipeline等批量操作的,如果非要使用批量操作,需要自己维护key与slot的关系。
pipeline也不能保证批量操作中有命令执行失败了而中断,也不能让下一个指令依赖上一个指令,如果非要这样的复杂逻辑,建议使用lua脚本来完成操作。
Redis实现消息队列和延时队列 消息队列Redis的实现消息队列可以用list来实现,通过lpush与rpop或者rpush与lpop结合来实现消息队列。
但是若是list为空后,无论是lpop还是rpop都会持续的获取list中的数据,若list一直为空,持续的拉取数据,一是会增加客户端的cpu利用率,二是也增高了Redis的QPS,解决方案是使用blpop或brpop来代替lpop或rpop。
其实blpop和brpop的作用是bloking pop,就是阻塞拉取数据,当消息队列中为空时就会停止拉取,有数据后立即恢复拉取。
但是当没有数据的时候,阻塞拉取,就会一直阻塞在那里,时间久了就成了空闲连接,那么Redis服务器一般会将时间闲置过久的连接直接断掉,以减少连接资源。所以还要检测阻塞拉取抛出的异常然后进行重试。
另外一点,就是Redis实现的消息队列,没有ACK机制,所以想要实现消息的可靠性,还要自己实现当消息处理失败后,能继续抛回队列。
延时队列用Redis实现延时队列,其实就是使用zset来实现,将消息序列化成一个字符串(可以是json格式),作为为value,消息的到期处理时间做为score,然后用多线程去轮询zset来获取到期消息进行处理。
多线程轮询处理,保证了可用性,但是要做幂等或锁处理,保证不要重复处理消息。