一、缓存更新策略
二、主动更新
三、过期策略
3.1 如何知道过期?
3.2 删除策略
四、内存淘汰策略
数据淘汰策略-使用建议
一、缓存更新策略
Redis是基于内存的数据库,它的优点就是在对数据进行操作的时候快,并且通过多种方案而形成的承载能力高。其缺点也非常的明显,那就是它不像基于磁盘的数据库一样可以一直存储大量的数据,所以我们需要定期的更新Redis中的数据以保证数据不会冗余,并且都是最新的数据。
关于内存淘汰、超时剔除、主动更新在文章中都会详细的讲到
内存淘汰 | 超时剔除 | 主动更新 | |
---|---|---|---|
说明 | 不用自己维护,利用Redis的内存淘汰策略,当内存不足时自动淘汰部分数据。下次查询时更新缓存。 | 给缓存数据添加TTL时间,到期后自动删除缓存。下次查询时更新缓存。 | 编写业务逻辑,在修改数据库的同时,更新缓存。 |
一致性 | 无 | 低 | 高 |
维护成本 | 无 | 低 | 高 |
在上述表格中列举了三种缓存更新的策略,其中保证一致性最高的无疑是主动更新,其在写业务逻辑时就主动更新Redis中的缓存数据。其次就是超时剔除,在过期之后直接查询数据库也能在一定程度上保证查询到的数据的准确性,那么这三种缓存更新策略我们该如何的进行选择呢?
低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存;
高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存;
二、主动更新
根据上文,主动更新共有三种方法:
1. Cache Aside Pattern:由缓存的调用者,在更新数据库的同时更新缓存
2. Read/Write ThroughPattern:缓存与数据库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性
3.Write Behind cachingPattern:调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。
方案一是我们开发中常常用到的,因为二、三都会因为某些不稳定性因素从而导致一致性失效。
同时来看三个问题
关于先删除缓存还是先操作数据库这个问题,我们一般都会采用先操作数据库,在删除缓存的方法,因为先删除缓存在操作数据库,此时如果第二个线程进来会造成缓存中存在脏数据的情况。
但是无论这两种用哪一种方案,他都不是目前最优的解决方案
这个问题你也可以为如何保证双写一致性?答案是:延时双删
问题一:为什么要删除两次?
答:上文提到,先删除缓存在操作数据库会造成缓存中存在脏数据的情况,所以在操作完成数据库之后在删除缓存可以极大程度的保证缓存中没有脏数据
问题二:为什么要延迟?
答:当前数据库都是主从模式,需要等一段时间主服务器同步到从服务器
那么随之的问题也就来了,延时双删虽然在很大的程度上保证了双写一致性,但是由于是延时,这个时间不好控制,所以还是会有脏数据的可能性,这会导致不是强一致性,关于如何保证强一致性可以使用互斥锁,这里不多展开。
三、过期策略
在上文中我们提到过,Redis是有最大存储上限的,所以为了能存储更多更有用的数据,我们通常会对添加的key设置ttl(过期时间),以保证内存中不会有过多的冗余的key。
这里有两个问题需要我们思考:
- Redis是如何知道一个key是否过期呢?
- 是不是TTL到期就立即删除了呢?
3.1 如何知道过期?
我们来看一下redisDb,我们都知道Redis中有0-15这16个库,而每一个库都类似于redisDb这样的结构体,他有如下属性
那么第一个问题的答案也呼之欲出了,即利用两个Dict分别记录key-value对及key-ttl对
3.2 删除策略
在redis中,一个key过期了并不是会立即删除的,这样对性能的开销是非常严重的,我们需要监视每一个key的ttl,这是不能接受的,所以redis提供了两种删除策略:惰性删除与定期删除。
惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放
周期删除:每隔一段时间,我们就对一些key进行检查,删除里面过期的key,即从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key
执行周期有两种:
Redis会设置一个定时任务serverCron(),按照server.hz的频率来执行过期key清理,模式为SLOW
Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理,模式为FAST
SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的 hz 选项来调整这个次数
FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
四、内存淘汰策略
当我们的项目非常庞大的时候,内存中的数据量可能会非常的多,此时如果仅仅删除过期的key是可能不够的,所以当Redis内存使用达到设置的阈值时,Redis会主动挑选部分key删除以释放更多内存的流程,就叫内存淘汰机制。
Redis支持8种不同策略来选择要删除的key:
- noeviction:不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略,通过修改配置文件修改
- volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
- allkeys-random:对全体key,随机进行淘汰。也就是直接从db->dict中随机挑选
- volatile-random:对设置了TTL的key,随机进行淘汰。也就是从db->expires中随机挑选
- allkeys-lru:对全体key,基于LRU算法进行淘汰
- volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
- allkeys-lfu:对全体key,基于LFU算法进行淘汰
- volatile-lfu:对设置了TTL的key,基于LFI算法进行淘汰
LRU(Least RecentlyUsed):最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
LFU(Least FrequentlyUsed):最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
数据淘汰策略-使用建议
- 优先使用 alkeys-ru 策略。充分利用 LRU 算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
- 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用alkeys-random,随机选择淘汰
- 如果业务中有置顶的需求,可以使用 volatile-lru 策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
- 如果业务中有短时高频访问的数据,可以使用 allkeys-lfu 或 volatile-lfu 策略。
最后我们来思考两个问题
问题1:根据数据淘汰策略,如何保证Redis中的数据都是热点数据?
答:使用allkeys-lru(挑选最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据。
问题2:Redis的内存用完了会发生什么?
答:主要看数据淘汰策略是什么?如果是默认的配置(noeviction),会直接报错。