Skip to content

Commit 6d9344a

Browse files
author
代码风水师
committed
缓存穿透与缓存雪崩
1 parent c9f5b18 commit 6d9344a

5 files changed

Lines changed: 164 additions & 9 deletions

File tree

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,10 @@
118118
- [Redis(第03篇)核心:持久化之RDB与AOF](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/cache/Persistence.md)
119119
- [Redis(第04篇)核心:主从复制与故障转移](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/cache/Replication.md)
120120
- Redis(第05篇)核心:高可用与 Sentinel 哨兵
121-
- Redis(第07篇)核心:分布式缓存与 Redis Cluster
122-
- Redis(第08篇)核心:缓存倾斜与热点key
123-
- Redis(第09篇)核心:缓存击穿(缓存穿透)
124-
- Redis(第10篇)核心:缓存雪崩
125-
- Redis(第11篇)核心:布隆过滤
126-
- Redis(第12篇)核心:缓存降级
121+
- Redis(第06篇)核心:分布式缓存与 Redis Cluster
122+
- [Redis(第07篇)核心:缓存击穿(缓存穿透)、缓存雪崩](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/cache/CachePenetration.md)
123+
- [Redis(第08篇)核心:热点key](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/cache/HotKey.md)
124+
- Redis(第09篇)核心:布隆过滤
127125

128126
### 九、:telescope::tokyo_tower::satellite:分布式系统
129127

@@ -135,7 +133,7 @@
135133
* 分布式系统 (第 06 篇) 精讲:Paxos算法(强一致性算法)
136134
* 分布式系统 (第 07 篇) 精讲:Chubby 与 Zookeeper
137135
* [分布式系统 (第 08 篇) 精讲:一致性哈希算法](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/distribution/ConsistentHashing.md)
138-
* 分布式系统 (第 09 篇) 精讲:设计分布式锁
136+
* [分布式系统 (第 09 篇) 精讲:设计分布式锁](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/distribution/DistributedLock.md)
139137
* 分布式系统 (第 10 篇) 精讲:分布式session/token一致性设计
140138
* 分布式系统 (第 11 篇) 精讲:分布式事务
141139
* 分布式系统 (第 12 篇) 精讲:分布式高并发的支持与控制
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">缓存击穿(缓存穿透)</h3>
2+
3+
![缓存穿透](https://i.loli.net/2019/02/19/5c6b968d1e946.png)
4+
5+
**缓存(Cache)** 是分布式、高并发的场景下,为了保护后端数据库,降低数据库的压力,引入的一种基于内存的数据访问机制,能加快数据的读取与写入,进而提高系统负载能力。
6+
7+
**缓存击穿(缓存穿透)** 是访问不存在缓存中的数据,进而直接访问数据库。
8+
9+
10+
11+
**缓存击穿****缓存穿透** 更为详细的分类:
12+
13+
| 名称 | 缓存是否存在被访问数据 | 数据库是否存在被访问数据 |
14+
| -------- | ---------------------- | ------------------------ |
15+
| 缓存击穿 |||
16+
| |||
17+
18+
在缓存穿透这种情形下,如果缓存和数据库都没有需要被访问的数据,那么访问缓存时没有数据则直接返回空,避免重复访问数据库,造成不必要的后端数据库压力。
19+
20+
21+
22+
总之,避免缓存击穿也好,避免缓存穿透也罢,核心思路是想法设法不要让请求怼在数据库上。
23+
24+
25+
26+
##### 解决方案
27+
28+
**1.缓存空值:** 如果第一次访问一个不存在的数据,那么将此key与value为空值的数据缓存起来,下次再有对应的key访问时,缓存直接返回空值,通常要设置key的过期时间,再次访问时更新过期时间;
29+
30+
**2.布隆过滤:** 类似散列集合(hash set),判断key是否在这个集合中。实现机制在于比特位,一个key对应一个比特位,并且存储一个标识,如果key有对应的比特位,并且标识位表示存在,则表示有对应的数据。比如使用Redis 的 Bitmap实现。
31+
32+
33+
34+
---
35+
36+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">缓存雪崩</h3>
37+
38+
**缓存雪崩:** 大量的缓存击穿意味着大量的请求怼在数据库上,轻者造成数据库响应巨慢,严重者造成数据库宕机。比如缓存内的数据集体定时刷新、服务器重启等。
39+
40+
41+
42+
##### 解决方案
43+
44+
1. 降低缓存刷新频率;
45+
2. 部分缓存刷新,刷新数据时按照一定规则分组刷新;
46+
3. 设置key永远不过期,如果需要刷新数据,则定时刷新;
47+
4. 分片缓存,在分布式缓存下,将需要缓存的数据 散列 分布到多个节点,尽量将热点数据均匀分布到多节点。
48+
49+
50+
51+
52+
53+
54+
55+
56+

resource/markdown/cache/HotData.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

resource/markdown/cache/HotKey.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">Redis热点Key</h3>
2+
3+
在一定时间内,被频繁访问的key称为 **热点key** 。比如突发性新闻,微博上常见的热搜新闻,引起千千万万人短时间内浏览;在线商城大促活动,消费者比较关注的商品突然降价,引起成千上万消费者点击、购买。热点key所在服务器的压力骤然上升,如果超出物理机器的承载能力,则缓存不可用,进而诱发缓存击穿、缓存雪崩的问题。
4+
5+
![缓存击穿](https://i.loli.net/2019/02/19/5c6b968d1e946.png)
6+
7+
8+
9+
#### 解决方案
10+
11+
**1. 读写分离:** 读写分离比较适用于写少读多的情景,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,93 @@
1+
比如某个应用需要定时跑一些任务,为了保证高可用,做了三个节点的集群,后来发现集群中每个节点都跑了重复的任务。
2+
3+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">一、基于数据库实现分布式锁</h3>
4+
5+
#### 1.1 悲观锁
6+
7+
首先创建一张表,用于存储锁标识的记录:
8+
9+
```mysql
10+
CREATE TABLE IF NOT EXISTS distribution_lock(
11+
id BIGINT(20) UNSIGNED NOT NULL COMMENT '分布式锁id',
12+
lock_name VARCHAR(50) DEFAULT '' COMMENT '分布式锁的名称',
13+
node_number TINYINT(1) NOT NULL COMMENT '集群下节点的编号,用于重入锁',
14+
gmt_create DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '行记录创建时间',
15+
PRIMARY KEY (id)
16+
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储分布式锁的表';
17+
```
18+
19+
20+
21+
**获取锁:**
22+
23+
```mysql
24+
INSERT INTO distribution_lock(id, lock_name, node_number) VALUES(1001, 'order_task', 1);
25+
```
26+
27+
28+
29+
**获取重入锁:**
30+
31+
```mysql
32+
SELECT
33+
t0.id,
34+
t0.lock_name,
35+
t0.gmt_create
36+
FROM distribution_lock t0
37+
WHERE t0.id = 1001 AND t0.node_number = 1;
38+
```
39+
40+
41+
42+
**锁超时:**
43+
44+
```mysql
45+
SELECT
46+
t0.id,
47+
t0.lock_name,
48+
t0.gmt_create ,
49+
TIMESTAMPDIFF(SECOND, t0.gmt_create, NOW()) 'duration_time'
50+
FROM distribution_lock t0
51+
WHERE t0.id = 1001 AND t0.node_number != 2;
52+
```
53+
54+
55+
56+
**释放锁:**
57+
58+
```mysql
59+
DELETE FROM distribution_lock WHERE id = 1001;
60+
```
61+
62+
63+
64+
> 上面是用 `INSERT` + `SELECT ` + `DELETE` 来实现的,当然也可以使用 `UPDATE` + `SELECT` 实现(注意初始化锁标识的行记录)。
65+
66+
67+
68+
#### 1.2 乐观锁
69+
70+
71+
72+
---
73+
74+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">二、基于Redis实现分布式锁</h3>
75+
76+
##### 悲观锁
77+
78+
79+
80+
##### 乐观锁
81+
82+
83+
84+
---
85+
86+
<h3 style="padding-bottom:6px; padding-left:20px; color:#ffffff; background-color:#E74C3C;">三、基于Zookeeper实现分布式锁</h3>
87+
88+
##### 悲观锁
89+
90+
91+
92+
##### 乐观锁
193

0 commit comments

Comments
 (0)