思路
每一个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过期,抢到锁的线程从数据源获取数据,再更新缓存,其他未获取锁的线程获取老数据返回

最新文章

  1. DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(1)
  2. viewController的自动扩展属性导致TableViewGroupStyle时向上填充
  3. 【P1304】【P1305】选课与选课输出方案
  4. 单独删除std::vector <std::vector<string> > 的所有元素
  5. Redis 一:安装篇
  6. mac os x 10.9.1 安装 Homebrew软件包管理工具及brew安装maven3.1.1
  7. NS2仿真:公交车移动周期模型及性能分析
  8. win7 虚拟机 ios开发环境搭建
  9. Jquery页面中添加键盘按键事件,如ESC事件
  10. 使用jquery.form.js提交表单上传文件
  11. spring mvc 中自定义404页面在IE中无法显示favicon.ico问题的解决方法。
  12. Mahout决策森林
  13. InnoDB存储引擎结构介绍
  14. Python从入门到放弃Day01
  15. 换行符java去除字符串中的空格、回车、换行符、制表符
  16. 大数据学习-2 认识Hadoop
  17. PowerBI新功能: PowerBI多报表共享一个数据集
  18. HTML--思维导图
  19. 使用 resizableImageWithCapInsets 方法实现可伸缩图片
  20. 第一章连通性问题-----algorithm in C 读书笔记

热门文章

  1. VC串口通讯,WriteFile或ReadFile没有任何返回??
  2. elementUI表单验证
  3. shell 大型脚本工具开发实战
  4. nginx的gzip模块详解以及配置
  5. Ant环境安装
  6. Python标准库3.4.3-urllib.request-21.6
  7. reGeorg+Proxifier使用
  8. 【新品发布】智能驾驶实车测试系统-VDAS
  9. 2019招商银行M-Geeker线上比赛题解析
  10. python高级特性-filter