为了账号安全,请及时绑定邮箱和手机立即绑定

redis 失效的key造成的问题

redis 失效的key造成的问题

PHP
猛跑小猪 2019-03-15 06:36:25
是这样得 //这里面执行判断是否存在缓存 $res = redis -> get("xxx"); if($res != null){ //返回缓存结果 return $res;//缓存返回 } $res = db -> query("select * ...")//假设这句要3秒 redis -> setex("xxx",3600,$res)//保存到缓存,3600时间 //返回查询结果 return $res; 大概意思是这样的,就是我先从缓存取,没有就从数据库取,且存到缓存中,方便下次再去取。 但是问题就来了,假设我有千万并发,xxx 这个key存在3600,3600时间后自动销毁,必须得重新从数据库获取,而这句sql要3秒,3秒钟并发了几千万,就是查了几千万次数据库,必须得第一条成功缓存下来,后续的才不会跳过缓存返回那句,但这期间3秒却足以使服务器垮了,我想问的是怎样才能只访问一次数据库,其他那几千万访问都是从缓存取。
查看完整描述

8 回答

?
蛊毒传说

TA贡献1895条经验 获得超3个赞

补充:

缓存被“击穿”问题(以前看过一篇文章的做法,你可以借鉴一下):
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。类似下面的代码:

 public String get(key) {
      String value = redis.get(key);
      if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
          if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
               value = db.get(key);
                      redis.set(key, value, expire_secs);
                      redis.del(key_mutex);
              } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                      sleep(50);
                      get(key);  //重试
              }
          } else {
              return value;      
          }
  }
查看完整回答
反对 回复 2019-03-18
?
LEATH

TA贡献1936条经验 获得超6个赞

你这个问题就是比较经典的缓存穿透问题了,可以考虑两个方法
1.该key永不过期,写个定时脚本定期刷新结果,写入该key
2.或者加个访问锁。缓存更新期间的时候,set(lock, 1),然后其他请求发现lock=1,给个友好提示什么的,更新成功后,lock置为0。大原则上,宁愿用户体验差一些,也不能因为缓存穿透雪崩导致宕机。

查看完整回答
反对 回复 2019-03-18
?
蝴蝶不菲

TA贡献1810条经验 获得超4个赞

脚本离线去刷缓存

查看完整回答
反对 回复 2019-03-18
?
波斯汪

TA贡献1811条经验 获得超4个赞

3600秒自动销毁,你可以在3500秒的时候重新生成缓存,在缓存失效前刷新缓存。

查看完整回答
反对 回复 2019-03-18
?
慕容3067478

TA贡献1773条经验 获得超3个赞

并发导致的问题,首先考虑锁的机制来解决问题,可以使用redis 的setnx实现锁的,保证每次只有一个请求load数据,数据更新完,进行解锁

查看完整回答
反对 回复 2019-03-18
?
慕容森

TA贡献1853条经验 获得超18个赞

可以尝试Gearman

查看完整回答
反对 回复 2019-03-18
  • 8 回答
  • 0 关注
  • 1363 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信