文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一日一技:实现有过期时间的LRU缓存

2024-12-03 00:27

关注

在一日一技:实现函数调用结果的 LRU 缓存一文中,我们提到Python自带的LRU缓存lru_cache。通过这个装饰器可以非常轻松地实现缓存。

现在我们考虑下面这个应用场景:MongoDB中有100对id-用户名的对应关系,我从Redis中持续不断读取id,如果id能在MongoDB中找到对应关系,那么就把对应的用户名打印出来。如果找不到对应关系,那么就把这个id丢弃。

为了防止频繁读取MongoDB,我在程序开始的时候直接读取这一百对对应关系,并存为字典:

  1. import pymongo 
  2. import redis 
  3.  
  4. client = redis.Redis() 
  5. handler = pymongo.MongoClient().weibo.id_name_map 
  6.  
  7.  
  8. def read_id_name_map(): 
  9.     id_name = {} 
  10.     for row in handler.find(): 
  11.         id_name[row['id']] = row['name'
  12.     return id_name 
  13.  
  14.  
  15. id_name_map = read_id_name_map() 
  16. while True
  17.     data = client.blpop('weibo_id'
  18.     user_id = data[1].decode() 
  19.     if user_id in id_name_map: 
  20.         print(id_name_map[user_id]) 

大家可以思考一下,上面这段代码有没有什么问题。然后继续看后面。

如果我现在需要再增加100个id-用户名的对应关系怎么办?

由于这个程序运行以后就一直阻塞式地读取Redis,不会停止,所以整个过程只会读取一次MongoDB。后面即使我向MongoDB中添加了新的对应关系,只要程序不重启,就无法读取到新的对应关系。

肯定有同学想到,在while循环里面增加一个计时器,每x分钟就重新调用一下read_id_name_map()函数,更新对应关系。

不过今天我们要讲的是另一个更有创意的办法,使用lru_cache来实现。

对于这个例子来说,lru_cache的maxsize参数只需要设置为1,因为只需要存放1份对应关系即可。那么我们如何做到,比如每10分钟更新一次呢?我们知道,在使用lru_cache时,如果调用同一个函数,并且传入的参数相同,那么从第二次开始就会使用缓存。现在我们如何让时间在每10分钟内相同呢?

我们来看现在的时间戳:1578399211.30042

它除以600,值是1578399211.30042 // 600 = 2630665.0。然后我让这个时间戳加5分钟,也就是增加300秒,变成1578399511.30042。这个新的时间戳再除以600,发现结果还是2630665.0。但如果原来的时间戳增加超过10分钟,例如增加了601秒,我们再来看看效果(1578399211.30042 + 601) // 600 = 2630666.0,此时的结果也发生了变化。

利用这个特点,修改一下我们的代码:

  1. import pymongo 
  2. import redis 
  3. import time 
  4. from functools import lru_cache 
  5.  
  6. client = redis.Redis() 
  7. handler = pymongo.MongoClient().weibo.id_name_map 
  8.  
  9.  
  10. @lru_cache(maxsize=1) 
  11. def read_id_name_map(_): 
  12.     id_name = {} 
  13.     for row in handler.find(): 
  14.         id_name[row['id']] = row['name'
  15.     return id_name 
  16.  
  17.  
  18. while True
  19.     data = client.blpop('weibo_id'
  20.     id_name_map = read_id_name_map(time.time() // 600) 
  21.     user_id = data[1].decode() 
  22.     if user_id in id_name_map: 
  23.         print(id_name_map[user_id]) 

现在,我们直接在while循环内部调用read_id_name_map,如果两次调用的时间间隔小于600秒,那么time.time() // 600的值是相同的,第二次直接使用缓存,也就不会查询MongoDB了。当时间超过10分钟后,时间戳除以600的值增加了,于是缓存没有命中,进入查询MongoDB的过程,更新id_name_map。实现了有过期时间的LRU缓存。

补充:可能有同学注意到定义read_id_name_map函数的时候,参数我写的是下划线。这是Python 编码规范中建议的一种写法。当一个变量不会被使用,但又需要保留时,就可以用下划线表示。

本文转载自微信公众号「未闻Code」,可以通过以下二维码关注。转载本文请联系未闻Code公众号。

 

来源:未闻Code内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯