3、缓存

厨子大约 6 分钟数据库原创面试题Redis程序厨

3.缓存

3.0 缓存分类

只读缓存:缓存只负责读,任何写操作都会直接操作在数据库上,然后删除缓存里的数据,然后下次再请求数据时,发现缓存内没有,则重新请求数据库,这样缓存内存在的数据仍为新数据。

读写缓存:读写缓存,顾名思义,其写操作也会落到缓存上,会在缓存中进行修改,但是我们要考虑这个情况,那就是我们修改了缓存,数据库宕机的情况。则会造成数据不一致的情况。

然后写入数据库的时机有两种形式,一种是同步写回,一种是异步写回。

同步写回则是,缓存修改之后,立刻修改数据库,这样可能降低响应速度,但是能够保证安全性,这里我们就需要使用事务来搞定。一起执行才行,不然就会产生数据不一致的情况。

异步写回:则会缓存修改之后,找机会再进行修改数据库。

3.1 缓存淘汰机制

在设置了过期时间的数据中进行淘汰

  • 随机淘汰(random)

  • LRU(随机选取N个值,然后在这N个值里选择最久未使用的)

  • LFU(使用次数最少)

  • TTL根据过期时间淘汰,越早过期的越删除

所有范围内进行淘汰

  • LRU

  • LFU

  • 随机

如何处理被淘汰的数据

如果数据是干净数据,则直接淘汰,如果数据是脏数据,则需要先写入数据库,然后再进行淘汰,不过Redis 使用的是修改完之后,直接写入数据库,所以不会出现清理缓存的时候出现脏数据的情况。

推荐阅读:https://zhuanlan.zhihu.com/p/343963744open in new window

3.2 如何解决缓存和数据库内容不一致的问题

一致性

缓存中有数据,那么需要和数据库一致

缓存中没有数据,那么数据库中的就是新数据

单线程环境下

消息队列

我们可以使用消息队列来搞定,将待修改的值存入消息队列,如果修改成功则删除消息队列里的数据,如果失败,则从消息队列中取出,然后继续修改。含义则是重试

我们平常开发中,应该优先使用先更新数据库再删除缓存的方法,这样能够减小数据库压力。因为先删缓存会有缓存缺失的情况。

多线程环境下

这种情况,则会出现新的问题,我们思考一下,当A线程修改了数据库,然后写入缓存,此时B线程又修改了数据库,则会导致缓存中存的仍然是旧值。还会有另外三种情况,如果在并发环境下,如果是读写问题,则会对业务影响不大,但是写写问题则会影响很大,所以这里我们可以借助分布式锁redlock来解决该问题。

推荐阅读:https://www.jianshu.com/p/a8eb1412471fopen in new window

3.3 缓存穿透,缓存雪崩,缓存击穿

缓存雪崩

是指缓存中大量键到期,而查询量过大,发现缓存中没有该键,则会去请求后台,引起数据库压力过大甚至宕机。

解决方案

  • 过期时间设置为随机,这样就不会出现短时间内大量过期的情况。

  • 使用分布式数据库,分别请求不同的数据库,则会减少某一数据库的压力。

  • 设置热点数据永远不过期

  • 核心数据可以访问数据库,非核心直接返回,进行一个降级

缓存击穿

缓存击穿是指,针对某个访问非常频繁的热点数据的请求,无法在缓存中进行处理,紧接着,访问该数据的大量请求,一下子都发送到了后端数据库,导致了数据库压力激增,会影响数据库处理其他请求。

  • 设置热点数据永远不过期

缓存穿透

缓存雪崩和缓存击穿都是因为键过期的情况,虽然缓存中没有该数据了,但是数据库中还有,虽然会对数据库造成压力,但是还是有机会解决。

情况如下,我们访问缓存时,发现缓存中没有该数据,则去查找数据库,发现数据库中也没有该数据,这样就会给缓存和数据库造成很大的压力。

发生这种事情有两种情况

误删除:被管理员一不小心将缓存中和数据库的数据都删了

恶意攻击:另一种情况就是被别人恶意攻击,故意拿数据库和缓存中没有的数据进行发出请求。

  • 设定空值和缺省值

  • 使用布隆过滤器判断是否含有该键,多用于缓存更新较少的情况,因为需要同步更新布隆过滤器

  • 入口处检测(前端界面过滤,过滤掉部分)

注:布隆过滤器的原理:使用N个哈希函数,得出N个哈希值,并将哈希表的N个位置标记成 1,如果已经为 1 则无需标记,判断值是否存在时,计算N个哈希值,对应的位是否存在 0 ,存在 0 则表明该值不存在。

推荐阅读:https://www.cnblogs.com/xuanyuan/p/13665170.htmlopen in new window

3.4 缓存污染

缓存污染的含义:某些不常被访问的数据,白白浪费内存空间

解决方法则是利用我们的缓存淘汰策略。我们来分析一下各个缓存淘汰策略能不能解决缓存污染的情况

  • LRU 不能解决,他可以解决最久未访问的键,但是有些不常用的键,可能会偶尔被访问一次。

  • LFU最近最少使用,在使用时间和使用次数上都有要求,所以则可以解决缓存污染的情况。

LFU原理,使用一个字符串,然后前半部分保存时间戳,后半部分保存使用次数。

3.5 缓存的过期删除策略

定期删除:Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存。

惰性删除:当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再有请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据。