本篇文章是基于极客时间蒋德钧老师的redis核心技术与实战redis缓存篇做的学习笔记。
为什么需要做缓存?
- 提高请求效率,数据库操作效率不良,请求效率也不良
- 减轻数据库压力,在高并发系统当中,大量请求直接压到数据库层会给数据库造成压力,请求过大可能造成数据库崩溃
redis提供了高效的数据存储功能,所以被广泛的应用到了缓存场景当中,既能有效地提升业务应⽤的 响应速度,还可以避免把⾼并发⼤压⼒的请求发送到数据库层。
redis是将数据缓存在内存当中,内存与外存的成本不一样,所以当然不能将所有数据缓存在redis当中,这就可能存在缓存满了的问题。
具体来说,我们需要掌握以下几点
- 缓存特征?
- redis缓存是怎么工作的?
- redis缓存满了怎么办?有哪些替换策略?
- 为什么会有缓存⼀致性、缓存穿透、缓存雪崩、缓存击穿等异常,该如何应对?
- Redis的内存毕竟有限,如果⽤快速的固态硬盘来保存数据,可以增加缓存的数据量,那么,Redis缓存可 以使⽤快速固态硬盘吗?
缓存特征
- 缓存一定是一个快速子系统,数据存在缓存当中,能够避免每次从慢系统中读取数据,数据库为慢系统,redis为快系统,redis的操作效率多为数据库操作效率的十倍以上。
- 缓存系统的容量大小总是小于后端慢速系统的,不可能把所有数据放到缓存系统中,redis具备缓存淘汰策略,所以也是redis适合做缓存的一个特征。
Redis处理缓存请求
缓存系统中都是先查redis,后查数据库
两个关键术语
缓存命中:读取redis,查询到缓存
缓存缺失:读取redis,查询缓存为空
redis称为旁路缓存,读取缓存、读取数据库和更新缓存的操作都需要在应用程序中完成
缓存类型
redis是不会区分缓存类型的,由于redis作为缓存所有的操作都是应用程序完成的,所以为了更好的在不同的情况下处理数据,可以人为的思考该缓存为那种类型,执行不同的逻辑
只读缓存:最新数据在数据库
- 查询数据:缓存命中,返回数据,缓存缺失查询数据库
- 修改数据:所有的请求,直接发往数据库修改数据库,修改完,缓存存在,删除缓存。再次读取数据,缓存缺失,读取数据库,重新缓存
读写缓存:最新数据在redis
所有读写请求都会发送给缓存,直接在redis中增删改操作。因为最新数据在redis当中,容易出现数据丢失,所以根据业务应用对数据可靠性和缓存性能的不同要求,有同步直写和异步写回两种策略。
同步直写:写请求发给缓存的同时,也会发给后端数据库进行处理,等到缓存和数据库都写完数据,才返回。
- 优点:缓存系统发生故障,数据库依旧是最新数据,提供了数据可靠性。
- 缺点:降低缓存的访问性能。
异步写回:所有请求先在缓存中执行,缓存中执行完直接返回。等到被修改过的缓存在缓存中淘汰出来时,缓存将他们写回数据库(redis缓存淘汰机制并不会主动写回数据库,所以选择异步写回的时候需要在客户端完成写回操作)。
- 优点:响应延迟低,redis缓存操作速度快
- 缺点:缓存系统出现故障,容易出现数据丢失
缓存类型选择 - 对写请求进行加速,选择读写缓存
- 写请求少或者提升请求响应速度,选择只读缓存
缓存淘汰策略
8中淘汰策略 (4.0之前六种,4.0之后八种)
内存使用超过maxmemory值,会根据配置执行相关淘汰策略
- noevction:不淘汰。redis不再提供服务,不再处理请求,直接返回错误。
- volatile-random:设置了过期时间随机淘汰。
- volatile-ttl:设置了过期时间,越早过期的越先被淘汰。
- volatile-lru:使用LRU算法(最近最少使用原则)赛选设置了过期时间的键值对
- volatile-lfu:使用LFU算法赛选设置了过期时间的键值对
- allkeys-random:所有键值对中随机选择淘汰。
- allkeys-lru:使⽤LRU算法在所有数据中进⾏筛选。
- allkeys-lfu:使⽤LFU算法在所有数据中进⾏筛选
如何处理淘汰的数据
前面提到了缓存的写回策略,只有读写缓存的异步写回需要写回数据库。
由于redis淘汰缓存是不会把他们写回数据库的,所以没有办法实现淘汰数据写回数据库这个操作,还是采用同步直写的方式写回数据库。
LRU算法
lru算法最近最少使用的原则来筛选数据,最不常用的数据会被筛选出来。
lru算法是以链表方式组织所有数据的,最近被访问的元素会被放到链表的头,最久未被访问的数据会被放到链表的尾,当有数据插入时,内存不够,会将新数据插入到链表头,删除链表尾数据。
缺点:
链表产生额外开销
当链表中大量数据被访问时,带来很多链表移动操作,会很耗时,降低redis缓存性能。
redis中lru简化
redis提供一个配置参数maxmemory-samples来配置redis选出的淘汰缓存个数组成链表。
redis会默认记录每个数据的最近一次访问的时间戳存放在键值对lru字段记录。
执行以下命令,redis会选出100个数据当候选数据集
config set maxmemory-samples 100
当内存不够有新的数据加入时,redis会筛选能进入候选集合的数据的lru字段的值必须小于候选集合中的最小的lru值,当有新数据进入达到maxmenory-samples时会将最小的lru数据淘汰出去
优点:这样没用每次维护一个大链表,也不用每次访问数据的时候都移动链表项,提升了缓存性能。
lfu算法解决缓存污染
LFU缓存策略是在LRU策略的基础商,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用LFU算法筛选出淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出缓存。如果两个数据访问次数相同,会根据两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。
使用哪种缓存淘汰策略?
业务数据有明显的冷热之分:allkeys-lru算法
无冷热之分:allkeys-random算法
有置顶需求(永远不需要删除):使用volatile-lru算法,并且不设置过期时间(永远不会过期)