缓存穿透、击穿、雪崩区别和解决方案

文中 cache 指缓存,比如 redis,db 指数据库,比如mysql。

一,缓存一般有三种模式

这里主要指的是 应用代码 对 cache 和 db 中数据的维护方式。

1.1  Cache Aside

应用代码同时更新 cache 和 db。

a) 数据写入 流程

b) 数据读取 流程

1.2  Read/Write Through

应用代码只更新 cache,cache 负责同步更新 db。

此时可以将 cache 和 db 看成一个整体,db 自己维护 cache。

1.3.  Write Back Caching

应用方代码更新缓存,另外将 cache 中数据异步定期更新到 db。

类似于 Linux 文件系统中的 page cache。

这个可能会导致数据不一致,甚至数据丢失,具体能不能用,得看业务的特点。

高性能和数据一致性在方案设计时要做取舍,根据业务特点选择。

二,缓存使用要注意的问题

当缓存使用不当时,可能会导致请求瞬间打到 db ,db 扛不住挂掉。

常见的有以下三种问题。

2.1   缓存穿透

概念说明: 指 cache 和 db 中都没有数据 ,读完 cache 没有,再读 db 还是没有,每次都请求到 cache 和 db。

解决方法:

a). 拦截非法请求,比如不正常的 id 请求直接拒绝。

b). 没有数据时也 cache 下,过期时间可设置短点,不把过多请求打到 db 去。

c). 使用  Write Behind Caching 模式,命中不了 cache 不读取 db。这时需要注意要使用的 cache 的大小,要缓存太多数据可能不合适。

d) 采用布隆过滤器,不存在的key直接过滤;布隆过滤器判断存在,不一定是真存在,但如果判断不在里面一定是不存在。

一般建议用前两种,实现起来比较简单。

2.2  缓存击穿

概念说明: 一般是 少量热点数据过期 (比如微博热搜数据) ,导致大量请求查  cache 没有,又查 db。

解决方法:

a) 热点key永不过期,异步更新。

b) 更新热点key时加锁,同一个key最好只有一个请求打到 db。

c) 接口限流,熔断、降级。

2.3  缓存雪崩

概念说明: 大量 cache 数据同时过期 ,导致大量 请求打到 db。

解决方法:

a) 热点key永不过期,异步更新。

b) 防止同时大量数据过期,cache 过期时间随机抖动下。

三,学习了这些有什么用?

在工作中使用到 cache 的地方,设计的时候要注意思考这些问题。避免大量请求打到 db ,导致系统 down 掉。

另外我们在阅读一些源码的时候,也可以使用到这些知识。 我曾经在 github 上提问过?

w = ts – (ts % period) + (zlib.crc32(value) % period)

这一行代码如何理解?

详见:https://github.com/jsocol/django-ratelimit/issues/120

Staggering windows

To avoid a situation where all limits expire at the top of the hour, windows are automatically staggered throughout their period based on the key value. So if, for example, two IP addresses are hitting hourly limits, instead of both of those limits expiring at 06:00:00, one might expire at 06:13:41 (and subsequently at 07:13:41, etc) and the other might expire at 06:48:13 (and 07:48:13, etc).

翻译下成大白话

比如我们都是限制了不同的ip,一个小时能请求1000次, 但由于窗口交错(加了抖动),就实现了这样的效果:

ip1 可能是 06:13:41 缓存过期,然后是 07:13:41。

而另外一个 ip2 则可能是 06:48:13 过期,然后是 07:48:13。

不同的 ip 过期时间固定,但不相同,从而有 效地避免了缓存雪崩问题。

四,思考

想一想,如果让你设计一个 rate limit 模块,你觉得会遇到上面的哪些问题呢?

你如何实现加个时间窗口,让缓存不要一起过期,从而不引起雪崩问题呢?

参考链接:

https://github.com/jsocol/django-ratelimit/blob/b300136651172c4edbaf278473996789d8a3ae02/ratelimit/core.py#L182