常见面试题
生产上你们的redis内存设置多少?
如何配置、修改redis的内存大小
如果内存满了你怎么办
redis清理内存的方式?定期删除和情性删除了解过吗
redis缓存淘汰策略有哪些?分别是什么?你用那个?
redis的LRU了解过吗?请手写LRU算法手写coc
Iru和lfu算法的区别是什么
如何设置修改redis
查看Redis最大占用内存
redis默认内存多少可以用?
何设置修改?般生产上你如何配置?
如何修改redis内存设置
什么命令查看redis内存使用情况?
#maxmemory
打开redis配置文件,设置maxmemory参数,maxmemory是bytes字节类型,注意转换。
默认是0
127.0.0.1:6379> config get maxmemory
“maxmemory”
2)“0”
如果不设置最大内存大小或者设置最大内存大小为0在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB
内存
注意,在64bit系统下,maxmemory设置为0表示不限制Redis内存使用
般生产上你如何配置?般推荐Redis设置内存为最大物理内存的四分之三
可以
通过修改文件配置
通过命令修改 临时的
什么命令查看redis内存使用情况?
info memory
config getmaxmemory
真要打满了会怎么样?
如果Redis内存使用超出了设置的最大值会怎样?改改配置,故意把最大值设为1个byte试试
127.0.0.1: 6379> config get maxmemory
1))“maxmemory”
②)“1”
127.0.0. 1: 6379> set k1 v1111111
(error) ooM command not allowed when used memory >‘maxmemory’
设置了maxmemory的选项,假如redis内存使用达到上限
没有加上过期时间就会导致数据写满maxmemory
为了避免类似情况,引出下一章内存淘汰策略
?它如何删除的?
redis过期键的删除策略
三种不同的删除策略
Redis不可能时时刻刻遍历所有被设置了生存时间的key,来检测数据是否已经到达过期时间,然后对它进行删
除。
立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会
随之释放。但是立即删除对cpu是最不友好的。因为删除操作会占用cpu的时间,如果刚好碰上了cpu很忙的时
候,比如正在做交集或排序等计算的时候,就会给cpu造成额外的压力,让CPU心累,时时需要删除,忙死。
这会产生大量的性能消耗,同时也会影响数据的读取操作。
总结:对CPU不友好,用处理器性能换取存储空间(拿时间换空间)
数据到达过期时间,不做处理。等下次访问该数据时,
如果未过期,返回数据;
发现已过期,删除,返回不存在。
情性删除策略的缺点是,它对内存是最不友好的。
如果一个键已经过期,而这个键又仍然保留在redis中,那么只要这个过期键不被删除,它所占用的内存就不会释放。
在使用情性删除策略时,如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话, 那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB),我们甚至可以将这种情况看作是一种内存泄漏-无用的垃圾数据占用了大量的内存,
而服务器却不会自已去释放它们,这对于运行状态非常依赖于内存的Redis服务器来说,肯定不是一个好消息
总结:对memory不友好,用存储空间换取处理器性能(拿空间换时间)
开启性淘汰,lazyfree-lazy-eviction=yes
定期删除策略是前两种策略的折中:
定期删除策略每隔一段时间执行一次删除过期键操作并通过限制删除操作执行时长和频率来减少删除操作对CPU时间的影响。
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
特点1:CPU性能占用设置有峰值,检测频度可自定义设置
特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
总结:周期性抽查存储空间(随机抽查,重点抽查)
举例:
redis默认每隔100ms检查是否有过期的key,有过期key则删除。注意:redis不是每隔100ms将所有的key检查一次而是随
机抽取进行检查(如果每隔100ms,全部key进行检查,redis直接进去ICU)。因此,如果只采用定期删除策略,会导致很多
key到时间没有删除。
定期删除策略的难点是确定删除操作执行的时长和频率:如果删除操作执行得太频繁或者执行的时间太长,定期删除策略就会
退化成立即删除策略,以至于将CPU时间过多地消耗在删除过期键上面。如果删除操作执行得太少,或者执行的时间太短,定
期删除策略又会和情性删除束略一样,出现浪费内存的情况。因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率。
上述步骤都过堂了,还有漏洞吗?
1定期删除时,从来没有被抽查到
2情性删除时,也从来没有被点中使用过
上述两个步骤======>》大量过期的key堆积在内存中,导致redis内存空间紧张或者很快耗尽
必须要有一个更好的兜底方案..
redis缓存淘汰策略登场
MAXMEMoRY PoLICY:how Redis will select what to remove when maxmemory
is reached. You can select one from the following behaviors:
volatile-Lru-> Evict using approximated LRu,only keys with an expire set.
volatile-lfu-> Evict using approximated LFu, only keys with an expire set.allkeys-Lru->Evict any key using approximated LRU.
allkeys- lfu -> Evict any key using approximated LFU.volatile-random->Remove a randomkey having an expire set.
allkeys-random->Remove a random key,any key.
volatile-ttl->Remove the key with the nearest expire time (minor TTL)
noeviction-> Don’t evict anything.just return an error on write operations.
LRU:最近最少使用页面置换算法,淘汰最长时间未被使用的页面,看页面最后一次被使用到发生调度的时间长短,首先淘汰
最长时间未被使用的页面。
LFU:最近最不常用页面置换算法,淘汰一定时期内被访问次数最少的页,看一定时间段内页面被使用的频率,淘汰一定时期
内被访问次数最少的页
举个栗子
某次时期Time为10分钟,如果每分钟进行一次调页,主存块为3,若所需页面走向为2121234
假设到页面4时会发生缺页中断
若按LRU算法,应换页面1(1页面最久未被使用),但按LFU算法应换页面3(十分钟内,页面3只使用了一次)
可见LRU关键是看页面最后一次被使用到发生调度的时间长短,而LFU关键是看一定时间段内页面被使用的频率!
1.noeviction:不会驱逐任何key,表示即使内存达到上限也不进行置换,所有能引|起内存增加的命令都会返回error
2.allkeys-Iru:对所有key使用LRU算法进行删除,优先删除掉最近最不经常使用的key,用以保存新数据
3.volatile-Iru:对所有设置了过期时间的key使用LRU算法进行删除
4.allkeys-random:对所有key随机删除
5.volatile-random:对所有设置了过期时间的key随机删除
6.volatile-ttl:删除马上要过期的key
7.allkeys-Ifu:对所有key使用LFU算法进行删除
8.volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除
2个维度
过期键中筛选
所有键中筛选
LRU
LFU
4个方面
random
ttl
在所有的key都是最近最经常使用,那么就需要选择allkeys-lru进行置换最近最不经常使用的
key如果你不确定使用哪种策略,那么推荐使用allkeys-lru
·如果所有的key的访问概率都是差不多的,那么可以选用allkeys-random策略去置换数据
·如果对数据有足够的了解,能够为key指定hint(通过expire/ttl指定),那么可以选择volati
le-ttl进行置换
如何配置、修改
config 临时
直接redis.conf配置文件
redis缓存淘汰策略配置性能建议
避免存储bigkey
开启性淘汰,lazyfree-lazy-eviction=yes
常见面试题
在深入技术细节之前,我们先来看几个高频的面试问题,这些问题也是我们在生产环境中必须面对的:
- 生产上你们的Redis内存设置多少?
- 如果内存满了你怎么办?
- Redis的过期删除和内存淘汰策略有哪些?
- LRU和LFU算法的区别是什么?
Redis内存配置与监控
在讨论如何“节流”之前,我们先要学会如何“开源”——即如何为Redis配置和监控内存。
如何设置Redis最大内存 (maxmemory
)?
Redis允许我们通过maxmemory
参数来限制其最大可用内存。
查看当前配置:
127.0.0.1:6379> CONFIG GET maxmemory 1) "maxmemory" 2) "0"
默认值为
0
,在64位操作系统下表示不限制内存使用;在32位系统下,则最多使用3GB。修改方式:
- 配置文件修改 (永久生效) :打开
redis.conf
文件,添加或修改配置maxmemory <bytes>
。例如maxmemory 1gb
。 - 命令修改 (临时生效) :通过
CONFIG SET maxmemory <bytes>
命令动态修改。例如CONFIG SET maxmemory 1024mb
。
- 配置文件修改 (永久生效) :打开
生产建议: 通常推荐将Redis的
maxmemory
设置为物理内存的3/4,为操作系统和其他进程预留足够的空间。
使用 INFO memory 命令可以获取详细的内存使用报告。
127.0.0.1:6379> INFO memory
# Memory
used_memory:1041376
used_memory_human:1016.97K
used_memory_human
字段直观地显示了当前占用的内存大小。
内存占满会发生什么?
如果设置了maxmemory
,当Redis的内存使用达到上限时,任何尝试写入新数据的命令都会返回一个错误,除非你配置了后续将要介绍的内存淘汰策略。
# 故意将最大内存设为1字节
127.0.0.1:6379> CONFIG SET maxmemory 1
(error) OOM command not allowed when used memory > 'maxmemory'
过期键的删除策略
我们经常为键设置过期时间(TTL),但Redis并不会在键过期的瞬间就立即删除它。因为它需要在CPU性能和内存占用之间做出权衡。Redis结合了两种主要的删除策略。
1. 惰性删除 (Lazy Deletion)
“等你需要时,我再检查。”
- 工作方式:数据到达过期时间后,Redis不做任何处理。直到下一次有客户端访问这个键时,Redis会先检查其是否过期。如果已过期,则执行删除并返回
nil
(不存在);如果未过期,则正常返回数据。 - 优点:对CPU极其友好,因为它只在必要时才进行检查和删除,避免了大量的CPU周期消耗在“巡逻”上。(拿时间换空间)
- 缺点:对内存极不友好。如果大量过期键从未被再次访问,它们将永远滞留在内存中,如同“内存泄漏”,白白占用宝贵的存储空间。(拿空间换时间)
2. 定期删除 (Periodic Deletion)
“我会定期抽查,但不会检查每一个。”
- 工作方式:惰性删除的内存风险太大,因此Redis引入了定期删除作为补充。Redis会每隔一段时间(默认100ms),从设置了过期时间的键空间中随机抽取一部分键进行检查,并删除其中的过期键。
- 优点:这是前两种策略的折中。它通过限制执行频率和时长,减少了对CPU的影响,同时也能周期性地释放过期键占用的内存,缓解了内存压力。
- 缺点:由于是随机抽查,它无法保证所有过期键都被及时删除。总会有一些“漏网之鱼”。
总结:Redis通过惰性删除确保最终一致性(访问时肯定会被清理),通过定期删除尽力维持内存的健康状态。但这两种策略的组合仍然存在漏洞: 如果一个键既没有在定期删除中被抽到,也再也没有被客户端访问,它依然会成为内存中的“僵尸数据” 。当这类数据堆积过多,就需要最终的兜底方案登场了。
内存淘汰策略 (Eviction Policies)
当Redis内存使用达到maxmemory
上限时,如果此时仍有写入操作,内存淘汰策略就会被触发,决定应该“牺牲”哪些数据来为新数据腾出空间。
Redis提供了8种不同的淘汰策略,可以从键的范围(所有键 vs. 仅设置了过期时间的键)和淘汰算法两个维度来划分:
策略 | 描述 |
---|---|
noeviction | 默认策略。不淘汰任何数据,任何可能导致内存增加的写命令都会返回错误。 |
allkeys-lru | 从所有键中使用近似LRU算法进行淘汰。 |
volatile-lru | 从设置了过期时间的键中使用近似LRU算法进行淘汰。 |
allkeys-lfu | 从所有键中使用近似LFU算法进行淘汰。 |
volatile-lfu | 从设置了过期时间的键中使用近似LFU算法进行淘汰。 |
allkeys-random | 从所有键中随机淘汰。 |
volatile-random | 从设置了过期时间的键中随机淘汰。 |
volatile-ttl | 从设置了过期时间的键中,淘汰剩余生存时间(TTL)最短的键。 |
核心算法解析:LRU vs. LFU
LRU (Least Recently Used - 最近最少使用)
- 核心思想:淘汰最长时间未被访问的数据。它关注的是数据最后一次被访问的时间点。
- 优点:实现简单,能有效处理热点数据。
- 缺点:无法很好地处理“偶发性高频访问”后长期不用的数据。比如一个数据在几分钟内被访问了100次,然后一天都未被访问,它依然可能比一个1小时前被访问过1次的数据“更不容易”被淘汰。
LFU (Least Frequently Used - 最近最不常用)
- 核心思想:淘汰在一段时间内访问次数最少的数据。它关注的是数据在一段时间内的访问频率。
- 优点:能更精确地反映数据的热度,避免了LRU的上述缺陷。
- 缺点:实现相对复杂,需要为每个键维护一个访问计数器。
举例说明:
假设内存已满,需要淘汰一个页面。页面访问序列为 2, 1, 2, 1, 2, 3, 4
。
- 按LRU:淘汰 页面1。因为在页面4需要调入时,页面1距离上一次被访问的时间最长。
- 按LFU:淘汰 页面3。因为在过去一段时间内,页面3只被访问了1次,频率最低(页面2访问3次,页面1访问2次)。
生产环境配置建议
如何选择淘汰策略?
-
allkeys-lru
(推荐) :如果你不确定用哪种,这是个很好的通用选择。它假设最近访问的数据未来也更可能被访问。 -
allkeys-lfu
:如果你的业务场景中,数据的访问频率比访问时间更能体现其价值,那么LFU是更优选择 (Redis 4.0+)。 -
volatile-ttl
:如果你能通过设置不同的过期时间来区分数据的优先级,这个策略非常有用。 -
allkeys-random
:如果你的数据访问模式非常平均,没有明显的热点,随机策略性能最好。
-
性能优化建议
- 避免存储Big Key:一个巨大的键(如几MB的Hash或String)在被淘汰时可能会导致Redis短暂阻塞。
- 开启惰性释放 (Lazy Freeing) :在
redis.conf
中设置lazyfree-lazy-eviction=yes
。这会让Redis在淘汰键时,将内存释放操作放到后台线程执行,避免阻塞主线程,提升性能。
总结
Redis的内存管理是一个分层、立体的防御体系。它首先通过惰性删除和定期删除主动清理过期数据,当内存压力触及上限时,再启动内存淘汰策略作为最终的保障。深刻理解这些机制,并根据业务场景做出合理的配置,是确保Redis服务稳定、高效运行的关键所在。
面试题回答
Q1: 生产上你们的Redis内存一般设置多少?
A: 一般设置为服务器物理内存的75% 。这样可以为操作系统本身的开销、后台任务(如RDB持久化时的fork操作)以及其他可能运行的进程预留足够的内存,防止因内存不足导致OOM Killer或严重的Swap性能下降。
Q2: 如果Redis内存满了会怎么样?
A: 这取决于你的maxmemory-policy
配置。
- 如果是默认的
noeviction
,任何会导致内存增加的写命令(如SET
,LPUSH
)都会立即返回一个OOM (Out of Memory) 错误。 - 如果配置了其他淘汰策略(如
allkeys-lru
),Redis会先尝试淘汰一个或多个键来腾出空间,然后再执行写命令。如果无法腾出足够空间,依然会返回错误。
Q3: Redis的过期删除策略和内存淘汰策略有什么区别?
A:
- 目的不同:过期删除是为了释放那些“逻辑上”已经无效的键所占用的内存。内存淘汰是在物理内存不足时,为了给新数据腾出空间,被迫删除某些“逻辑上”仍然有效的键。
- 触发时机不同:过期删除由“键被访问时(惰性)”或“周期性检查时(定期)”触发。内存淘汰只在内存使用达到
maxmemory
上限,并且有新的写入请求时才会触发。 - 本质:过期删除是主动清理,内存淘汰是被动防御。
Q4: LRU和LFU算法的区别是什么?你推荐用哪个淘汰策略?
A:
区别:
- LRU (最近最少使用) 关注的是时间:淘汰最长时间没有被访问过的数据。
- LFU (最不经常使用) 关注的是频率:淘汰在一段时间内访问次数最少的数据。LFU能更好地处理偶发性、短时高频访问后变冷的数据。
推荐策略:
-
allkeys-lru
是最通用、最安全的选择,适用于大多数业务场景。 - 如果你的业务能明确区分数据的热度,并且希望长期热门的数据不被偶然访问的冷数据挤掉,
allkeys-lfu
(Redis 4.0+) 是更优的选择。 - 如果数据都有明确的生命周期,
volatile-ttl
是一个很好的选择。
-