🎬 博客主页:楼下安同学的博客
🎥 本文由 楼下安同学 原创 🙉
🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📆生活明朗,万物可爱,人间值得,未来可期!✨
----❤️-----❤️-----❤️-----❤️-----❤️-----❤️-----❤️-----
前言
我们知道,现在的Web项目都是前后端分离的,后端负责业务逻辑的处理,前端负责渲染数据等,当一页中需要展示的数据量过大的时候,我们就要用到分页,分页不仅可以提高用户体验,更重要的是可以降低数据库的压力,分页算法就是利用limit
关键字来实现取数据,我们使用EXPLAIN
关键字查询SQL的执行效率,发现随着数据量越来越大,查询数据越来越慢,究其原因是limit关键字在每一次操作的时候还是会遍历前面的数据,所以效率缓慢,查询资料得知,业界解决这一问题的方法是使用缓存,就是说在用户点击指定页码的时候,我么可以拿到这一页的最后一条数据,这一条数据我们可以存起来,点击下一页的时候我们再查缓存中的那条数据,从缓存中的那条数据开始取,这样就可以提高查询的效率。
实际问题
继上文,我优化了分页算法,效率也提高了,可我们的数据是动态变化的啊,当我们向数据库新增一条数据的时候,缓存中的记录的数据还是没有改变,导致脏读
数据,这是不允许的,所以我们要实时删除缓存的数据,然而代码执行是有顺序的,我们不能保证在代码执行过程中缓存中有新的数据进来,导致脏读。
我们不能保证缓存数据和数据库实时都是一致的,但数据最终一致性是我们的结果,所以我们要做双删,就是再删一次,就能解决这一问题。
延时队列
延时队列实际上就是一个轮询任务,每隔多长时间执行一次,可以控制间隔时长,今天选择redis实现延时队列。
为什么用Redis做延时队列
- 消息持久化,消息至少被消费一次
- 实时性:存在一定的时间误差(定时任务间隔)
- 支持指定消息 remove
- 高可用性
- Redis 的特殊数据结构 ZSet 满足延迟的特性
实现
这里我们要测试脚本,由于导入的时候会执行一次,所以,我们分开写。
# delay_queue.py
import redis
import time
class DelayRedisQueue:
"""
基于 Redis延时队列 有序集合
"""
def __init__(self, key):
self.key = key
self.r = redis.Redis(decode_responses=True)
# 入队
def enqueue(self, uid, delay=0):
self.r.zadd(self.key, {uid: time.time() + delay})
# 出队
def dequeue(self):
res = self.r.zrangebyscore(self.key, 0, time.time(), start=0, num=1, withscores=False)
if res:
return res[0]
else:
return 0
# 删除延时任务
def remove(self, uid: int) -> None:
return self.r.zrem(self.key, uid)
# 入队脚本
from delay_queue import DelayRedisQueue
if __name__ == '__main__':
d = DelayRedisQueue("atx")
d.enqueue(1,6)
# 出队脚本
import time
from delay_queue import DelayRedisQueue
if __name__ == '__main__':
d = DelayRedisQueue("key")
while 1:
uid = d.dequeue()
if uid:
print(uid)
d.remove(uid)
time.sleep(1)
总结
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1),我们可以把当前的时间戳加上需要延长的时间作为score传入,利用zrangebyscore方法获取它就可以让它到那个时间点执行。