3383 字
约 11 分钟
1
JAVA 精髓面试题 · Redis 内存淘汰策略、LRU/LFU 与 bigkey

RedLock总结 Redlock 只有建立在「时钟正确」的前提下,才能正常工作,如果你可以保证这个前提,那么可以拿来 使用。 但是时钟偏移在现实中是存在的: 第一,从硬件角度来说,时钟发生偏移是时有发生,无法避免。例如, CPU 温度、机器负载、芯片材料 都是有可能导致时钟发生偏移的。 第二,人为错误也是很难完全避免的。 所以, Redlock尽量不用它,而且它的性能不如单机版 Redis,部署成本也高,优先考虑使用主从+ 哨兵 的模式 实现分布式锁(只会有很小的记录发生主从切换时的锁丢失问题)。 2、说一说Redis的内存淘汰策略 当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。

近似 LRU 算法 近似 LRU 算法 近似 LRU 算法 近似 LRU 算法 近似 LRU 算法 近似 LRU 算法 近似 LRU 算法

如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 如何采样就是看maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配 置,默认为 5。 采样数量越大,近似 LRU 算法的效果越接近严格LRU 算法。 同时 Redis3.0 在算法中增加了淘汰池,新算法会维护一个候选池(大小为16),池中的数据根据访问 时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中 最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访 问时间最大(最近被访问)的移除。进一步提升了近似 LRU 算法的效果。 Redis维护了一个24位时钟,可以简单理解为当前系统的时间戳,每隔一定时间会更新这个时钟。每个 key对象内部同样维护了一个24位的时钟,当新增key对象的时候会把系统的时钟赋值到这个内部对象 时钟。比如我现在要进行LRU,那么首先拿到当前的全局时钟,然后再找到内部时钟与全局时钟距离时 间最久的(差最大)进行淘汰,这里值得注意的是全局时钟只有24位,按秒为单位来表示才能存储194 天,所以可能会出现key的时钟大于全局时钟的情况,如果这种情况出现那么就两个相加而不是相减来 求最久的key。 LFU算法 LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根 据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。 LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法, 一个key很久没有被访问到, 只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被 访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热 点数据。LFU原理使用计数器来对key进行排序,每次key被访问的时候,计数器增大。计数器越大,可 以约等于访问越频繁。具有相同引用计数的数据块则按照时间排序。 LFU一共有两种策略: volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key allkeys-lfu:在所有的key中使用LFU算法淘汰数据 LFU把原来的key对象的内部时钟的24位分成两部分,前16位ldt还代表时钟,后8位logc代表一个计数 器。 logc是8个 bit,用来存储访问频次,因为8个 bit能表示的最大整数值为255,存储频次肯定远远不够, 所以这8个 bit存储的是频次的对数值,并且这个值还会随时间衰减,如果它的值比较小,那么就很容易 被回收。为了确保新创建的对象不被回收,新对象的这8个bit会被初始化为一个大于零的值LFU INIT_VAL (默认是=5)。 ldt是16个bit,用来存储上一次 logc的更新时间。因为只有16个 bit,所精度不可能很高。它取的是分 钟时间戳对2的16次方进行取模。 ldt的值和LRU模式的lru字段不一样的地方是, ldt不是在对象被访问时更新的,而是在Redis 的淘汰逻辑进行时进行更新,淘汰逻辑只会在内存达到 maxmemory 的设置时才会触发,在每一个指令的执行之前都会触发。每次淘汰都是采用随机策略,随 机挑选若干个 key,更新这个 key 的“热度”,淘汰掉“热度”最低的key。因为Redis采用的是随机算法, 如果 key比较多的话,那么ldt更新得可能会比较慢。不过既然它是分钟级别的精度,也没有必要更新得过于 频繁。 ldt更新的同时也会一同衰减logc的值。 ldt更新的同时也会一同衰减logc的值。 3、什么是BigKey?该如何解决 什么是bigkey bigkey是指key对应的value所占的内存空间比较大,例如一个字符串类型的value可以最大存到 512MB ,一个列表类型的value最多可以存储23-1个元素。 如果按照数据结构来细分的话, 一般分为字符串类型bigkey和非字符串类型bigkey。 字符串类型:体现在单个value值很大, 一般认为超过10KB就是bigkey,但这个值和具体的OPS相关。 非字符串类型:哈希、列表、集合、有序集合,体现在元素个数过多。 bigkey无论是空间复杂度和时间复杂度都不太友好,下面我们将介绍它的危害。 bigkey的危害 bigkey的危害体现在三个方面: 1、内存空间不均匀.(平衡):例如在Redis Cluster中, bigkey 会造成节点的内存空间使用不均匀。 2、超时阻塞: 由于Redis单线程的特性,操作bigkey比较耗时,也就意味着阻塞Redis可能性增大。 3、网络拥塞:每次获取bigkey产生的网络流量较大 假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB 的流量,对于普通的千兆网卡(按照 字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也 就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。 bigkey的存在并不是完全致命的: 如果这个bigkey存在但是几乎不被访问,那么只有内存空间不均匀的问题存在,相对于另外两个问题没有 那么重要紧急,但是如果bigkey是一个热点key(频繁访问),那么其带来的危害不可想象,所以在实际开发 和运维时一定要密切关注bigkey的存在。 发现bigkey redis-cli --bigkeys可以命令统计bigkey的分布。

但是在生产环境中,开发和运维人员更希望自己可以定义bigkey的大小,而且更希望找到真正的bigkey 都有哪些,这样才可以去定位、解决、优化问题。 判断一个key是否为bigkey,只需要执行debug object key查看serializedlength属性即可,它表示 key 对应的value序列化之后的字节数。

可以看到,第一次执行scan 0,返回结果分为两个部分:

JAVA 精髓面试题 · Redis 内存淘汰策略、LRU/LFU 与 bigkey
http://www.clxhxhhr.top/posts/1577/
作者
clxstart
发布于
2026-09-18
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。