一、多级缓存架构
在线上系统中,一定不会单纯的只部署一个Redis集群,而是使用Redis结合其他的多级缓存应用进行架构。
使用多级缓存架构的优点就是可以使不同类型的数据分布在不同的应用中,比如redis的热点key可以存储到nginx本地缓存、服务集群的本地缓存,进而分散单应用的服务压力。
二、redisObject
所有value对象在内部定义为redisObject结构体。
三、Redis的单线程
redis的单线程不是真的单线程,在内部执行命令的时候使用单线程,但是其他的操作夜壶开启其他的线程。
1、IO多路复用器
2、文件事件分派器
3、文件事件处理器
4、Redis 6 提供了多线程执行
四、Redis的缓存淘汰机制
当Redis的可用内存占满的时候,就会触发内存和磁盘的swap交换,导致Redis的性能急剧下降。这是绝对不能允许的,为了限制最大内存的使用,Redis使用参数:maxmemory限制Redis缓存的最大容量。当达到Redis缓存的限制,就触发缓存淘汰机制。
修改maxmemory-policy参数可以指定使用哪种缓存淘汰机制。可选项包括:
noeviction,
volatile-lru,
volatile-ttl,
volatile-random,
allkeys-lru,
allkeys-random,
volatile-lfu,
allkeys-lfu
查看当前配置的内存淘汰策略
config get maxmemory-policy
1、noeviction - 默认
不可驱动,当内存中的数据量达到配置的内存时,为了避免数据丢失,不可执行set操作,只能执行读操作、删除操作。
2、volatile-lru
lru - less read use
没设置过期时间的key不淘汰。
针对设置了过期时间的key,执行淘汰。
淘汰的原则是使用次数最少的key优先被淘汰。
3、volatile-ttl
没设置过期时间的key不淘汰。
针对设置了过期时间的key,执行淘汰。
淘汰的原则是哪个key剩余的存活时间最短,就淘汰哪个key。
4、volatile-random
没设置过期时间的key不淘汰。
针对设置了过期时间的key,执行淘汰。
随机淘汰策略。
5、allkeys-lru
针对所有key,使用最少的先淘汰。
6、allkeys-random
针对所有key,随机淘汰。
7、volatile-lfu
针对有过期时间的key,先淘汰访问频次少的key。
8、allkeys-lfu
针对所有key,先淘汰访问频次少的key。
五、缓存淘汰相关算法
1、LRU算法
最近使用原则。
通用的LRU算法是维护一个链表,将最近访问的key放到链表的头部。这样不经常访问的key就会被挤到链表的尾部,当执行淘汰的时候就从链表最尾部开始淘汰。
但是维护这样的链表会造成额外的开销,Redis为了保证响应的效率,没维护这样的链表,而是使用了【近似LRU算法】。
2、近似LRU算法
与LRU算法稍有不同,并不是真正维护了一个访问顺序链表,而是在每一个key上开辟了一块【24bit】的空间,记录着上一次访问的时间戳,通过比较时间戳的大小也能实现访问顺序链表的功能。
默认配置下,通过每次采样5个key,互相比较上次访问的时间戳,淘汰最早访问的key。直到内存数据小于maxmemory配置的大小。
采样数量配置参数:maxmemory-samples
采样范围:根据maxmemory-policy配置的淘汰策略决定。
3、LFU算法
Redis 4.0 版本之后新增了LFU算法,采样成功后再根据key最近被访问的频次决定淘汰哪些key,访问频次越少的先被淘汰。而不是单纯根据最近访问时间做判断。
为什么要这么做呢?场景一:当redis初始化的时候,所有的key基本都在同一时间加载,且越重要的越早加载,这时候不能盲目地根据最后访问时间决定淘汰哪些缓存。
那么是如何实现记录访问频次的呢?在key上的【24bit】空间的基础上,分成了【16bit】+【8bit】的空间,大的记录着最后访问时间戳(精度下降),小的记录着访问次数。
4、缓存时间戳
Redis在内存中缓存了一个时间戳,并不是用的时候从操作系统中获取当前时间戳,而是执行了一个定时任务,每1ms执行一次,从操作系统获取时间戳更新到Redis缓存中,用的时候直接从缓存中拿。
在redis中执行一条命令大约需要 100ns,所以每1ms是一个相对较大的时间单位。而每一次从操作系统获取时间戳时,上下文切换的时间消耗大约是 10us,所以redis选择了 “间隔一大段时间,才做一次时间戳更新” 。
六、Redis的过期策略
1、过期key的集合
在Redis中,设置了过期时间(ttl)的key,会被放入一个过期key的字典,从这个过期key的字典实现定期删除。
2、定时扫描策略
Redis创建了一个定时任务,每1s执行10次。
每次从过期字典中随机采集20个key,删除其中过期的数据。
如果发现达到 1/4 的key是过期的,就再采集20个key,删除过期数据,循环往复。
是一个贪心算法。
3、惰性删除
为了弥补定时扫描删除可能出现的“漏网之鱼”,当get一个key的时候,发现已经过期了,就不会返回value数据,同时也删除这个key。
4、从库的过期策略
没有定期扫描、惰性删除等功能。只是从主库的AOF文件中读取del命令来删除数据。
5、lazyFree
在删除数据或内存淘汰时,删除String数据基本会很快,但是在删除大的list,set,hash等数据时,有可能会很慢,造成阻塞。
为了缩短阻塞时间,Redis会将大体量的数据删除操作放到后台去做。
① 工作原理
Redis主线程进仅对key做个逻辑删除,标识某个key需要启用异步删除。lazyFree拿到这个key后,启动一个异步线程,对这个key做物理删除。从而避免主线程阻塞。
② 配置参数
参数 | 作用 |
---|---|
replica-lazy-flush | 集群启动 lazyFree 删除数据 |
slave-lazy-flush | 从节点启用 lazyFree 删除数据 |
lazyfree-lazy-user-flush | 用户输入 flush 命令删除 |
lazyfree-lazy-user-del | 用户输入 del 命令删除 |
lazyfree-lazy-server-del | 针对有些指令在处理已存在的键时,会带有一个隐式的DEL键的操作。 如rename命令,当目标键已存在,Redis会先删除目标键 |
lazyfree-lazy-eviction | 内存达到 maxmemory 的时候,触发缓存淘汰 |
lazyfree-lazy-expire | 针对 key 过期的删除 |