写点什么

数据库与缓存的一致性方案演进

用户头像
邱学喆
关注
发布于: 2021 年 03 月 27 日
数据库与缓存的一致性方案演进

一. 概述

在计算机体系,访问内存的速度比访问磁盘的速度要快很多;如果将数据保存到内存,性能会有指数级的提升;这就是缓存产生的原因;

常规情况下,将磁盘的数据保存到内存当中,当再次访问,发现内存有数据,那么就不用访问磁盘;在近几年随着 redis 的大量使用,很多业务都使用里 redis 来当作缓存系统使用,流程如下:



二. 问题及方案

2.1 缓存失效问题及解决方案

如果有数据变化,没有即时的清理缓存,则缓存的数据是脏数据,系统继续访问缓存的数据,就会有问题;需要一个后台服务定时清理该缓存数据;既然是定时任务,那么频率多少,才合适呢?一般来说根据业务场景来进行设计,常规的频率:天、星期、月等频率较低。如果频率过高的,会造成系统的吞吐量下降,不适用该流程。既然频率较低,那么可以衍生出这么一个依据:该数据发生变更,并不对业务有太大的影响,则适用该场景。如下图:


    

既然是采用 redis 来做缓存系统,可以采用 redis 的数据过期机制,设置 key 的过期时间,这样子交给 redis 自行缓存清理等。

2.2 缓存时效性问题及解决方案

如果数据库的数据变更频率过高或者不定期,或者注重时效性的,那么上图则不适用;因为数据库的数据一旦发生了变更,一直等定时任务被触发,才会将缓存清理,这样子的时效性较低;有些业务逻辑有这样要求,需要获取实时数据,并不是滞后的数据(也就是脏数据),业务逻辑对数据准确性有较强的依赖性。如果获取的是脏数据,就会影响对应的业务逻辑,就会出现生产事件。只要有任何变更,都即时清理或更新缓存;如下图:



2.3 查询并发性问题及解决方案

基于上一章节的流程图,进行讲解。当有两个请求过来,两个【查询数据】请求,一个是【更新数据】请求;当【更新数据】请求已经【删除 redis 数据】时,一个【查询数据】请求过来,发现 redis 没有缓存,则直接查询数据库,接着将查询到数据 A 放入 redis 中。这时【更新数据】请求已经更新数据,即将 A 改为 B。这时另外一个【查询数据】请求过来,发现 redis 有缓存,这时的数据是 A,直接返回;然而数据库里最新的数据是 B,就会出现业务异常行为;

基于这个并发性问题,我们可以在 redis 设置一个锁机制,来限制【redis 保存缓存】行为;流程如下:



补充一下,如果是多节点的服务器,可以考虑将锁放到 redis 服务器上;这个锁就是分布式锁。

2.4 更新并发性问题及解决方案

基于这个流程图,又有性能问题;如果同时有两个【更新数据】或者【删除数据】请求,就会出现阻塞状态;影响体验,是否有更好的处理方案呢?

需要重新设置一种锁机制,类似信号锁机制。信号量初始值为 0,当多个【更新数据】请求同时过来,信号量都+1, 执行完后-1。而【查询数据】请求,只有当信号量为 0 时,说明没有【更新数据】请求在更新缓存,说明数据库里面的数据是最新的。可以将数据库的数据保存到 redsi 中。



三. 缓存常见的问题

3.1 缓存穿透

访问一个不存在 key 时,缓存不起作用,请求会穿透到 DB,流量大时 DB 会挂掉。

我们可以采用布隆过滤器机制进行避免该问题的产生。

布隆过滤器是采用非常巧妙的概率型数据结构来判断该 key 是否存在问题。在缓存中,通过布隆过滤器过滤,发现 key 不存在,那么说明该 key 的缓存一定不存在;如果发现 key 存在,但并不表明该缓存一定存在;单节点,可以考虑使用 BitSet 类来当作布隆过滤器;多节点时,将该数据结构放在 redis 中。


在【查询数据】请求时,通过布隆过滤器进行判断该数据是否准在。可以减少对缓存以及数据库的压力;但里面有个问题,其布隆过滤器的设置,在哪个过程中进行初始化以及对 key 在布隆过滤器中的标志。换句话说,设置某个缓存在在布隆过滤器对应的位置进行标注,表明该缓存已经存在;可以在保存数据到数据库时,对布隆过滤器进行设置。



3.2 缓存击穿

一个存在的 key,在缓存失效的一刻,同时有大量的请求,这些请求都会击穿到 DB,造成 DB 瞬时请求量大、压力骤增问题。

可以考虑加锁机制,进行限流,减少对 DB 的访问压力。采用什么样的锁机制,可以评估有并发量的可能性;可以采用信号锁机制;



3.3 缓存雪崩

大量的 key 设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成 DB 瞬时请求量 da 、压力骤增,引起雪崩。

可以将其缓存的失效时间分开即可避免该问题的产生;

四. 汇总

根据上述的问题及解决方案,整理得出:

4.1 缓存数据时效性要求不高

可以采用定时清理缓存或者设置缓存失效机制去解决。只需要解决缓存穿透以及缓存击穿等问题即可。


4.2 缓存数据时效性高

在查询数据时,需要加两个锁,一个锁 A,针对解决缓存击穿问题;另外一个锁 B,是类似锁的机制(其实质是信号量),其解决的是查询并发性问题。



在更新、删除数据时,实时处理缓存数据;



PS. 这是初步设计方案,如有不合理的,欢迎发言;后续将以代码实现出来。


发布于: 2021 年 03 月 27 日阅读数: 18
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning. 原理的爱好,源码的阅读。以白话的形式输出我对原理以及源码解读的理解。

评论

发布
暂无评论
数据库与缓存的一致性方案演进