思路
每一个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. 无法解析的外部符号 ___argc nafxcw.lib(appcore.obj)

    参考 https://blog.csdn.net/wfree/article/details/44171157 MFC的使用: 由"在静态库中使用 MFC" 改成 "在共 ...

  2. 今日前端框架Vue学习笔记

    在线网页网址http://xingxunxinxi.com/StudentCourse/first.html代码 界面

  3. zabbix4.2的yum+mariadb方式部署安装

    本文依据官方文档操作(英文4.2):https://www.zabbix.com/documentation/4.2/manual/installation/install_from_packages ...

  4. Windows10 图标变白修复

    Windows10 图标变白修复 本文作者:天析 作者邮箱:2200475850@qq.com 发布时间: Tue, 16 Jul 2019 10:54:00 +0800 这种问题多半是ico缓存造成 ...

  5. echart——关系图graph详解

    VueEchart组件见上一篇 <template> <VueEcharts :options="options" auto-resize /> </ ...

  6. 【robotframework】robotframework基本使用

    一.创建项目 1.创建测试项目 选择菜单栏 file----->new Project Name 输入项目名称:Type 选择 Directory. 2.创建测试套件 右键点击“测试项目”选择 ...

  7. String Buffer和String Builder的区别(转)

    相信大家看到过很多比较String和StringBuffer区别的文章,也明白这两者的区别,然而自从Java 5.0发布以后,我们的比较列表上将多出一个对象了,这就是StringBuilder类.St ...

  8. Python 获取环境变量的几种方式

    第一种 import os os.environ.get('key_name') 第二种 import os os.getenv('key_name') 第三种 import os os.getenv ...

  9. python 元类理解

    原文来自:https://segmentfault.com/a/1190000011447445 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生万物 我是谁?我从哪来里?我要到哪里去? ...

  10. JavaScript中对数组的排序

    将下列对象数组,通过工资属性,由高到低排序 var BaiduUsers = [], WechatUsers = []; var User = function(id, name, phone, ge ...