Redis基础
缓存击穿
定义:缓存击穿(Cache Breakdown)是指在高并发访问某个缓存中的数据时,该数据刚好失效(缓存过期),导致大量请求直接打到数据库。
原因:发生在缓存过期的瞬间,因为并发访问很高,缓存失效后没有及时更新,导致大量请求直接访问数据库。
解决方案:
- 互斥锁(Mutex):在访问缓存时,如果发现缓存失效,使用互斥锁(如 Redis 的分布式锁)来确保只有一个线程能去加载数据并更新缓存,其他线程等待。
- 提前更新缓存:在缓存即将失效时,提前更新缓存数据,防止在高并发情况下出现击穿问题。
缓存穿透
定义:缓存穿透(Cache Penetration)是指大量请求的数据根本不存在于缓存和数据库中,导致所有请求直接打到数据库。
原因:由于恶意攻击或查询的数据本身不存在,从而绕过缓存层,直接访问数据库。
解决方案:
- 缓存空值:对于数据库中不存在的查询结果,缓存一个空值(null),并设置一个较短的过期时间。
- 布隆过滤器:在查询缓存前,通过布隆过滤器(Bloom Filter)快速判断数据是否存在,如果不存在则直接返回,避免访问数据库。
缓存雪崩
定义:缓存雪崩(Cache Avalanche)是指在同一时间大量缓存集中失效,导致大量请求直接打到数据库,可能导致数据库崩溃。
原因:主要是因为缓存集中设置了相同的过期时间,导致在某一时刻大量缓存同时失效。
解决方案:
- 缓存过期时间随机化:在设置缓存时,给每个缓存设置一个随机的过期时间,避免集中失效。
- 分布式缓存:使用分布式缓存系统,分散缓存存储,避免单点集中失效。
- 二级缓存:在缓存失效后,通过二级缓存(例如本地缓存)来缓解数据库压力。
内存淘汰策略
Redis 在内存达到上限时,会根据内存淘汰策略决定如何释放内存:
noeviction
:默认策略,不淘汰,并不再允许写操作allkeys-lru
:从所有键中使用 LRU 算法淘汰volatile-lru
:从设置了过期时间的键中使用 LRU 算法淘汰allkeys-random
:从所有键中随机淘汰volatile-random
:从设置了过期时间的键中随机淘汰volatile-ttl
:从设置了过期时间的键中,优先淘汰 TTL 较短的键volatile-lfu
:从所有键中使用 LFU 算法淘汰allkeys-lfu
:从设置了过期时间的键中使用 LFU 算法淘汰
LRU
LRU(Least Recently Used)最近最少使用算法,淘汰规则基于访问时间,淘汰最久未使用的元素。
LRU 通常双向链表 + 哈希表实现,但对 Redis 来说内存开销太大。
近似 LRU 策略
Redis 中使用了一种基于采样的近似 LRU 算法:
- 在 Dict 中维护每个 key 的上次访问时间;
- 进行内存置换时,随机采样若干 Key,淘汰其中最久未使用的键;
这种方法在性能上做了折中,使其接近 LRU 的真实淘汰行为。
LFU
LFU(Least Frequently Used)最不经常使用算法,淘汰规则基于访问频率,淘汰访问频率最低的元素。
近似 LFU 策略
Redis 中使用了一种近似的 LFU 计数器,通过概率更新和衰减机制实现:
-
使用 hashmap 为每个 key 维护一个 24 bits 的计数器;
-
当 key 被访问时,根据概率函数决定该 key 的访问频率是否需要增加;
-
进行内存置换时,随机采样若干 Key,淘汰其中访问频率最低的键;
-
通过定时任务,减少访问频率;