redis学习:延迟队列 异步消息队列
redis延迟队列使用zset有序列表来实现 对于如加锁失败的线程 如果一直轮询会消耗资源 但如果使用sleep那么遇到死锁时就会彻底瘫痪 所以会使用延迟队列。
当获取锁失败 会把消息序列序列化为字符串作为zset的value 把时间戳作为score (如time()+5 )多个线程对延迟队列进行轮询:lua脚本(网络往返从两次变为一次 并且多线程情况下因为lua原子性不会出现多个线程查询但只有一个线程能执行的现象) 每次zrangebyscore取出范围为0到当前时间戳的第一个value 然后zrem删除(抢到了) 之后处理业务逻辑。
消息队列:可以使用redis 的list作为消息队列 生产者使用lpush/rpush 消费者使用blpop/brpop b是blocking 就是阻塞读 在消息队列为空的时候 会进入休眠状态 当有任务会立即唤醒
但redis作为消息队列有一定的缺点1.redis数据在内存中 没有持久化时宕机数据即丢失 即使开启aof每秒刷盘 但还是可能会丢失一秒的数据 如果开启appendfsync always 那么又大大影响性能 无法支持高并发任务。 2.消息确认机制不足 不同于mq的ack/nack的消息确认机制 redis作消息队列本身没有确认机制 可能会导致重复消费或者消费失败后任务丢失 3.主从同步延时 redis主从异步复制 在故障切换时可能会丢失数据 如果redis主节点在写入或修改后 还没来得及同步到从节点就宕机 那么切换从节点后可能会丢失关键据
无法做到高一致性 4. 可能带来性能瓶颈 阻塞读需要创建tcp连接去监听队列 在高并发的情况下 大量tcp连接会占用大量资源 同时这些连接的建立 断开会带来cpu压力 如果brpop/blpop还有超时管理机制(超时将阻塞的客户端直接移除) 在高并发场景下会带来大量cpu压力( redis使用无序双向链表来存储时间事件 每次事件循环迭代,遍历时间事件链表,检查是否有事件到期 这会带来cpu消耗)