思路
每一个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. C# List 转 Tree 公共方法

    # 用C# 写了个List数据结构转树形数据结构的公共扩展方法 /// <summary> /// 将列表转换为树形结构 /// </summary> /// <type ...

  2. iPhone电话与短信相关代码小结

    关于iPhone上电话与短信相关功能,做一个简单总结: 使用公开SDK能实现的功能: (1)获取和操作通讯录.使用函数 ABAddressBookRequestAccessWithCompletion ...

  3. [LeetCode] 76. 最小覆盖子串 ☆☆☆☆☆(滑动窗口)

    https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong- ...

  4. c# 处理串行化对象的版本变化

  5. 使用华为云+GitHub搭建自己的博客

    1.搭建自己博客首先创建GitHub账号 在GitHub官网上创建一个账号:   我的账号地址是 : https://github.com/SueKayTian 2.GitHub账号创建好之后,客户端 ...

  6. LB_Kim

    LB_Kim 搜了一圈发现居然很少有博客说到这个东西, LB_Kim, 就是Kim设计的 下界函数 计算公式: LB_Kim(S,C)=MAX(i=1,2,3,4) d(∮(i)A,∮(i)C) em ...

  7. 链表实现队列(python)

    # -*- coding: utf-8 -*- from collections import deque class Node(object): def __init__(self, value=N ...

  8. 前端笔记-bom

    BOM对象 BOM即浏览器对象模型,它与dom不同的是可以操作浏览器窗口,使用它的接口我们可以改变窗口,状态栏,文本,及其他与除页面以外其他动作,使得js可以和我们浏览器进行沟通 窗口 即window ...

  9. NodeJS开发博客(二) 接入数据库

    1. mysql 数据库下载网址:https://dev.mysql.com/downloads/mysql/ 账号是 root 密码是 a1************ 网站账号是邮箱,密码是 Aa1* ...

  10. 在virtualenv中使用不同的Python版本

    来源:百度SEO公司   我有一个目前使用python 2.5.4运行的Debian系统. 我正确安装了virtualenv,一切正常. 我是否可以将virtualenv与其他版本的Python一起使 ...