设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。

缓存击穿

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

解决方案

有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

缓存雪崩

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

解决方案

缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间,所以这里给出两种版本代码参考:

public String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, * )
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他线程休息50毫秒后重试
Thread.sleep();
get(key);
}
}
}

优化后的互斥锁逻辑

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;
}
}

  下面是基于Memcache的add方法实现的分布式互斥锁

if (memcache.get(key) == null) {
// 3 min timeout to avoid mutex holder crash
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
value = db.get(key);
memcache.set(key, value);
memcache.delete(key_mutex);
} else {
sleep(50);
retry();
}
}

 参考链接:https://blog.csdn.net/zeb_perfect/article/details/54135506

基于(Redis | Memcache)实现分布式互斥锁的更多相关文章

  1. 基于Zookeeper实现的分布式互斥锁 - InterProcessMutex

    Curator是ZooKeeper的一个客户端框架,其中封装了分布式互斥锁的实现,最为常用的是InterProcessMutex,本文将对其进行代码剖析 简介 InterProcessMutex基于Z ...

  2. 基于redis实现的分布式锁

    基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...

  3. 单机Redis实现分布式互斥锁

    代码地址如下:http://www.demodashi.com/demo/12520.html 0.准备工作 0-1 运行环境 jdk1.8 gradle 一个能支持以上两者的代码编辑器,作者使用的是 ...

  4. 基于redis的简易分布式爬虫框架

    代码地址如下:http://www.demodashi.com/demo/13338.html 开发环境 Python 3.6 Requests Redis 3.2.100 Pycharm(非必需,但 ...

  5. 基于Redis的简单分布式锁的原理

    参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...

  6. 基于redis解决session分布式一致性问题

    1.session是什么 当用户在前端发起请求时,服务器会为当前用户建立一个session,服务器将sessionId回写给客户端,只要用户浏览器不关闭,再次请求服务器时,将sessionId传给服务 ...

  7. 基于Redis的开源分布式服务Codis

    Redis在豌豆荚的使用历程--单实例==>多实例,业务代码中做sharding==>单个Twemproxy==>多个Twemproxy==>Codis,豌豆荚自己开发的分布式 ...

  8. 基于 Redis 实现简单的分布式锁

    摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...

  9. 基于Redis实现简单的分布式锁【理论】

    摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...

随机推荐

  1. iframe 同域下父子页面的通信

    //共同引用的JS文件 common.js ; (function (window, $) { $(function ($) { window.trip = window.trip || {}; wi ...

  2. Python爬虫之三种网页抓取方法性能比较

    下面我们将介绍三种抓取网页数据的方法,首先是正则表达式,然后是流行的 BeautifulSoup 模块,最后是强大的 lxml 模块. 1. 正则表达式   如果你对正则表达式还不熟悉,或是需要一些提 ...

  3. MySQL权限操作:Grant、Revoke

    数据库操作: 创建数据库.创建表——CREATE 删除数据库.删除表——DROP 删除表内容——TRUNCATE.DELETE(后者效率低.一行一行地删除记录) 查询数据库.查询表——SELECT 插 ...

  4. 栈应用之 后缀表达式计算 (python 版)

    栈应用之 后缀表达式计算 (python 版) 后缀表达式特别适合计算机处理 1.  中缀表达式.前缀表达式.后缀表达式区别  中缀表达式:(3 - 5) * (6 + 17 * 4) / 3 17 ...

  5. IIS 启用https

    参考:http://www.cnblogs.com/dudu/p/iis_https_ca.html

  6. ZOJ 3962 Seven Segment Display(数位DP)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...

  7. POJ 1236 Network of Schools(tarjan求强连通分量+思维)

    题目链接:http://poj.org/problem?id=1236 题目大意: 给你一个网络(有向图),有两个任务: ①求出至少同时需要几份副本可以使得整个网络都获得副本 ②至少添加多少信息表(有 ...

  8. gtk+学习笔记(五)

    今天继续做的是昨天那个界面对的优化,直接贴下代码, void click_radio(GtkWidget *widget,gpointer *data) { 3 GtkWidget *dialog; ...

  9. springboot 1.5.X junit测试

    import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; impo ...

  10. 【POJ】1286.Necklace of Beads

    题解 群论,我们只要找出所有的置换群的所有循环节 具体可参照算法艺术与信息学竞赛 旋转的置换有n个,每一个的循环节个数是gcd(N,i),i的范围是0到N - 1 翻转,对于奇数来说固定一个点,然后剩 ...