思路
每一个key都有一个附属key1,附属key1可以是key加特定前缀组成,key对应value为真正的缓存数据,附属key1对应的value不重要,可以是随便一个值,附属key1的作用主要是维护缓存更新时间并保证只有一个线程到数据源拉取数据更新缓存
附属key1的过期时间设置为缓存刷新时间,比如30s,key的过期时间设置 缓存刷新时间 + 数据源修复预期时间(比如2天)
每次请求数据时,使用setnx(将 key 的值设为 value ,当且仅当 key 不存在)设置附属key1,返回结果为1:设置成功,代表附属key1过期,需要刷新数据,从数据源获取数据更新缓存,若返回结果为0:设置失败,代表附属key1未过期,不需要刷新数据,从缓存key中获取数据
由于redis是单线程,setnx操作相当与互斥锁,在并发情况下只有一个线程能获取到锁,杜绝了大量并发击穿缓存请求到数据库的问题
流程图

代码演示

package com.liutf.util;

import redis.clients.jedis.Jedis;

/**
* redis工具
**/
public class ReidsUtil { private static final String HOST = "192.168.11.23";
private static final int PORT = 6379; /**
* 附属key前缀
*/
private static final String PREFIX = "prefix:"; /**
* 数据源修复预期时间
*/
private static final int FIX_TIME = 2 * 26 * 60 * 60; /**
* 缓存时间过期时PREKEY_TIME的缓存时间
*/
private static final int PREKEY_TIME_COMMON = 30; private static Jedis jedis = null; static {
jedis = new Jedis(HOST, PORT);
} public static String get(String key) {
/**
* 组装设置附属key
*/
String prefixKey = PREFIX + key;
Long setnxResult = jedis.setnx(prefixKey, "1"); /**
* 附属key过期返回null,从数据源获取数据
* 附属key未过期,从key中获取数据
*/
if (setnxResult == 1) {
jedis.expire(prefixKey, PREKEY_TIME_COMMON);
return null;
} else {
return jedis.get(key);
}
} public static boolean set(String key, String value) {
/**
* 组装设置附属key
*/
String prefixKey = PREFIX + key;
jedis.setnx(prefixKey, "1");
jedis.expire(prefixKey, PREKEY_TIME_COMMON); jedis.set(key, value);
jedis.expire(key, PREKEY_TIME_COMMON + FIX_TIME);
return true;
} }
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;        

          }  

缺点分析

  1. 每次请求数据,就需要先操作附属key,再设置附属key过期时间,请求量在原有的两倍多
  2. 并发情况下,附属key过期,抢到锁的线程从数据源获取数据,再更新缓存,其他未获取锁的线程获取老数据返回

redis缓存击穿问题一种思路分享的更多相关文章

  1. $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )

    $.ajax()方法详解   jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...

  2. redis 缓存击穿 看一篇成高手系列3

    什么是缓存击穿 在谈论缓存击穿之前,我们先来回忆下从缓存中加载数据的逻辑,如下图所示 因此,如果黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义.如果 ...

  3. Redis缓存击穿

    缓存击穿 缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞. 比如在做 ...

  4. redis缓存击穿和缓存雪崩

    工作中经常会用到redis来做缓存,以防止后台db挂掉.但是db数据一般都在10T以上,不可能把mysql中的数据全部放入redis中,所以一般是将一些热key放入redis中. 缓存击穿 一个请求先 ...

  5. Redis缓存击穿、缓存穿透、缓存雪崩

    文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 上篇文章谈到了Redis分布式锁,实际上就是为了解释为什么做缓存采用Redis而不使用map/guava.缓存 ...

  6. Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?

    原始数据存储在 DB 中(如 MySQL.Hbase 等),但 DB 的读写性能低.延迟高. 比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS = 10000 左右,读写平均耗时 ...

  7. [Redis]处理定时任务的2种思路

    用redis完成类似 at 命令的功能,例如订单24小时后没有支付自动关闭,定时发邮件,主要说下任务生成之后怎么触发消费. 使用 有序集合 思路: 使用sorted Sets的自动排序, key 为任 ...

  8. 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )

    很多小伙伴没接触过Redis,以至于去学习的时候感觉云里雾里的,就有一种:教程随你出,懂了算我输的感觉. 每次听圈内人在谈论的时候总是插不上话,小编就偷偷去了解了一下,也算是初入门径. 然后就整理了一 ...

  9. 谈谈redis缓存击穿透和缓存击穿的区别,雪崩效应

    面试经历 在很长的一段时间里,我以为缓存击穿和缓存穿透是一个东西,直到最近去腾讯面试,面试官问我缓存击穿和穿透的区别:我回答它俩是一样的,面试官马上抬起头用他那细长的单眼皮眼睛瞪着我说:"你 ...

随机推荐

  1. 【转载】使用Jedis操作redis

    Redis是一个开源的Key-Value数据缓存,和Memcached类似. Redis多种类型的value,包括string(字符串).list(链表).set(集合).zset(sorted se ...

  2. springmvc+mybatis的增删改查入门

    先到官网了解mybatis的语法:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html 前端用了thymeleaf和vue.js,效果图和demo地址:ht ...

  3. iftop:Linux流量监测工具

    一.安装须知: (1)iftop 必须以root运行   (2)安装前首先安装:libpcap和libcurses 及包驱动ncurses-devel libpcap-devel (2.1)libpc ...

  4. MySQL的安装及简单配置

    一 .数据库概念 Mysql能干嘛呢? 它就是一款软件,安装在任何一台计算机或者服务器上的时候,只要我告诉它创建一个文件,新增一个数据,删除一个数据它就能帮我去做想要的操作 那我们暂且能不能理解为my ...

  5. iOS - UIWebView和WKWebView的比较和选择-作为H5容器的一些探究

    一.Native开发中为什么需要H5容器 Native开发原生应用是手机操作系统厂商(目前主要是苹果的iOS和google的Android)对外界提供的标准化的开发模式,他们对于native开发提供了 ...

  6. [转]預防 Android Dex 64k Method Size Limit

    转载自:http://ingramchen.io/blog/2014/09/prevention-of-android-dex-64k-method-size-limit.html 08 Septem ...

  7. stm32 视频小车

    1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ...

  8. Windows环境下实现Jenkins自动化部署

    详见:https://blog.csdn.net/Try_harder_every_day/article/details/79170065 Jenkins自动化部署: 几条具体的思路:1.开发人员将 ...

  9. C#为什么要装箱和拆箱

    来自论坛4楼的回答非常棒,地址:https://bbs.csdn.net/topics/390624164?page=1 内容如下: 道理很简单,按理说C#被设计成一种完全面向对象的语言.因此,包括数 ...

  10. Interval 用法总结

    语法:INTERVAL 'integer [- integer]' {YEAR | MONTH} [(precision)][TO {YEAR | MONTH}] 该数据类型常用来表示一段时间差, 注 ...