【redis缓存淘汰机制导读】redis作为一款内存型数据库,其设计的初衷就是为了给广大业务层提供高效的数据读、写能力,因为访问内存的速度肯定是要比直接访问磁盘的速度快几个数量级,假设业务方所有数据读、写请求全部都转发到后台的数据库,那么势必给数据库带来很大的压力,甚至可能导致数据库宕机。那么本文将为您介绍redis的缓存、以及缓存淘汰的机制。
redis用作缓存时,优先会把redis部署在数据库的前端,帮助数据库拦住大部分的读、写请求,redis缓存也会存在是否命中的情况,缓存并不能满足所有的业务数据请求。一般会有如下两种情况:
缓存命中:redis中有相关的数据,直接读取出来并返回给业务方。
缓存缺失:redis不存在相应的数据,那此时只能往后端的数据库去请求,同时也要把缺失的数据写回redis,相当于更新缓存,方便业务方下次请求数据的时候,能直接从缓存中获取。
从上文的描述中也能看出一些问题,假如缓存没有命中,业务方只能从后台数据库去查询相关数据,那此时缓存中缺失的数据该以什么样的方式去更新呢?是立即更新还是等待某个时机去更新,基于这种情况,redis推出了两种缓存机制。
只读缓存
只读缓存,顾名思义只提供数据查询服务,当业务方调用GET接口查询数据,存在则给业务方返回缓存中的数据;不存在则从数据库中去查;当业务方调用DEL、SET接口对数据进行删除、修改时,如果redis已经缓存了这类数据,redis会优先删除这些数据,随后直接去后台数据库删除、修改这些数据。当下次业务方再次请求这些被删除的数据,会发生缓存缺失,随后去数据库中查询,同时把数据库中这些数据更新到缓存中来。同学们可以结合下面的流程来理解只读缓存。
只读缓存的优势在于,数据库中能实时保存最新的业务数据,就算redis服务宕机了,最新的数据不会随着redis的宕机而丢失。
读写缓存
读写缓存,那便是业务方的读、写请求都直接在缓存中进行,并将缓存中的结果实时返回给业务方,但缓存中数据的更新并不及时写回数据库,这样就存在最新业务数据丢失的风险。那读写缓存中最新的业务数据是按照什么策略写回数据库的呢?redis提供"同步直写"和"异步写回"两种策略。
同步直写:写请求发给缓存,同时也会发给后端数据库进行处理,等到缓存和数据库都更新完,才给客户端返回。那样就严重拖慢了redis的相应速度了。
异步写回:写请求优先在缓存中处理。等这些新增或修改的数据要被从缓存中淘汰掉时,缓存将它们写回后端数据库。这样便不会影响redis响应速度了。
好,简单介绍完redis的缓存及类型,那继续讨论一个缓存淘汰的问题,假设redis主库接收到了大量的业务客户端请求,或者业务客户端消费速度过慢,导致redis输出缓冲区中积攒了大量的数据,甚至把缓存写满了,那这个时候该怎么办?接下来介绍缓存淘汰策略。
Redis 缓存有哪些淘汰策略?
我们根据淘汰候选数据集的范围把它们分成两类:
1、基于过期时间的淘汰机制:volatile-random、volatile-ttl、volatile-lru、volatile-lfu。
2、基于数据范围的淘汰机制: allkeys-lru、allkeys-random、allkeys-lfu。
Redis使用的内存空间超过maxmemory值时,redis便会按照一定的规则进行数据的淘汰,具体规则如下:
volatile-ttl: 针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
volatile-random:针对设置了过期时间的键值对,进行随机删除。
volatile-lru: 基于LRU算法筛选设置了过期时间的键值对,进行淘汰。
volatile-lfu: 基于LFU算法选择设置了过期时间的键值对,进行淘汰。
PS:针对LRU算法、LFU算法,笔者在后续的文章中做介绍。
那第二批淘汰策略,就更好理解些,基于所有的键值对,进行随机、采用LRU、LFU算法进行淘汰。
何为LRU算法?这也是面试中经常被问到的知识点,面试官可能要你手写个LRU算法出来,或者结合redis做提问。
LRU全称:Least Recently Used,即按照最近、最少使用的原则来筛选数据,最近频繁被使用的热数据会留在缓存中。那如何筛选?LRU把所有的数据组织成一个链表,链表的头和尾分别表示MRU端和LRU端,分别表示最近常使用和最近不常使用。可以看看如下示意图:
最新写入的数据15,会被塞到链表头部,如果当前内存中的容量上限就是5,那么为了插入15这个最新的数据,末尾端的5便要被筛选出来,进行淘汰。
那还存在一个问题,筛选出来的数据是直接把它删除掉吗?假设筛选出来的数据比数据库中的数据还要新,那么直接删除,势必会造成数据丢失。redis一般这样处理:一旦选中淘汰的数据后,如果这个数据是干净的,就直接删除;如果这个数据是脏的,redis把它写回数据库,如下图所示:
那如何判断数据是否是脏的?
和后端数据库中的数据保持一致,说明就是干净的数据,不一致就是脏数据,就是这么简单。