type
status
date
slug
summary
tags
category
password

1、Redis键空间

默认情况下,Redis 中有 16 个数据库,编号从 0-15。每个 Redis 数据库使用一个 redisDb 对象来表示,每个 redisDb 对象实际上是一个 dict 结构的数据集合。
其中 dict 保存了该 db 中的所有键值对。键值对中键是一个字符串对象,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象在内的任意一种 RedisObject。
notion image
redis 通过全局哈希表保存所有的 key 值,所以查找 key 的时间为 O(1)。redis 一下次写入太多数据有可能会产生哈希冲突,全局哈希表也是通过渐进式 rehash 来解决 hash 冲突的问题。

2、过期删除策略

过期删除策略就是指 key 过期后 Redis 会如何删除。我们可以设置 Redis 中缓存的 key 的过期时间。通常有以下策略:
过期策略
描述
优点
缺点
定时删除
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。
节省内存
CPU不友好,频繁的删除可能会影响性能
惰性删除
访问一个key时,会先判断该key是否已过期,过期则清除。
节省CPU资源,只在访问时进行过期检查,不会因为删除大量过期键而影响系统性能
如果键长期不被访问,即使已过期也会占用内存,导致内存浪费
定期删除
定期随机检查一批设置了过期时间的key,删除其中已过期的key
前两者的一个折中方案,使得CPU和内存资源达到最优的平衡效果。
需要找到合适的执行频率平衡点
Redis同时使用了惰性过期和定期过期两种过期策略
  1. 每当我们设置一个 key 的过期时间时,Redis 就会将该 key 带上过期时间存放到一个过期 dict 中。
  1. 当我们查询一个键时,Redis 便首先检查该 key 是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。
  1. Redis 默认会每 100ms 扫描一次过期 dict,但不会遍历过期 dict 中所有的 key,而是采用了一种简单的贪心策略。
    1. 从过期字典中随机选 20 个 key。
    2. 删除这 20 个 key 中已经过期的 key。
    3. 如果过期的 key 比率超过 1/4,那就重复步骤 1。

3、内存淘汰策略

内存淘汰策略是指在内存不足时如何删除 key 来释放空间。虽然我们有过期策略来淘汰已过期的数据,但是如果大量数据过期时间还没到,但此时内存已经快被占满了,我们就需要通过内存淘汰策略选择淘汰哪些数据。
内存淘汰策略有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。
notion image
内存淘汰策略
描述
适用场景
noeviction
默认策略,不淘汰数据,当内存不足时,新写入操作会返回错误(如 OOM command not allowed when used memory > 'maxmemory')。
确保数据不丢失,但需应用程序自行处理写入失败。
volatile-ttl
在设置了过期时间的键中,优先删除剩余存活时间最短的键。如果无过期键,则退化为 noeviction。
希望优先淘汰即将过期的键。
volatile-random
在设置了过期时间的键中,进行随机删除。
无明确访问规律,随机淘汰降低开销。
volatile-lru
在设置了过期时间的键中,使用 LRU 算法,删除最近最长时间不使用的key。Redis 对 LRU 的优化:每次随机选出N个数据,比较该N个数据的最近访问的时间戳,然后最长时间不访问的数据。
热点数据分布明显,希望保留高频访问的键。
volatile-lfu
在设置了过期时间的键值对,使用 LFU 算法,删除使用次数最少的key。
长期未被访问的冷数据优先淘汰。
allkeys-random
从所有键值对中随机删除。
无明确访问规律,随机淘汰降低开销。
allkeys-lru
在所有数据中进行筛选,使用 LRU 算法,删除最近最长时间不使用的key。
热点数据分布明显,希望保留高频访问的键。
allkeys-lfu
在所有数据中进行筛选,使用 LFU 算法,删除使用次数最少的key。
长期未被访问的冷数据优先淘汰。
LRU和LFU算法比较:
  • LRU(Least Recently Used):淘汰最长时间未被使用的页面,强调的是最近访问时间。链表按访问时间进行排序,表头是最近访问的页面。如果访问命中了页面,就把页面移动到链表表头;如果没有命中就从表尾删除页面,然后把新页面放在表头,是比较符合正常思维逻辑的算法。
  • LFU(Least Frequently Used):淘汰最近被访问次数最少的页,强调的是最近访问频率。链表按访问次数进行排序,表头是访问次数最多的页面。如果访问命中了页面,就把页面的访问次数加一,此时如果该页面的访问次数大于前一个页面就互相交换位置;如果没有命中页面就从表尾删除数据。这种算法有一个明显的问题:如果某页面开始访问次数较多,但后面没有再被访问,这些页面无法被删除。所以LFU算法用的比较少。
淘汰策略选择建议:
  • 优先使用 allkeys-lru 策略。如果你的业务数据中有明显的冷热数据区分,希望可以保存高频访问的热点数据,那么可以使用 allkeys-lru 策略。这样可以充分利用 LRU 这一经典缓存算法的优势,把最近最常访问的数据留在缓存中,提升应用的访问性能。
  • 如果业务应用中的数据访问频率相差不大,没有明显的冷热数据区分,建议使用 allkeys-random 策略, 随机选择淘汰的数据就行。
  • 如果你的业务中有置顶的需求,比如置顶新闻、置顶视频,那么,可以使用 volatile-lru 策略,同时不给这些置顶数据设置过期时间。这样一来,这些需要置顶的数据一直不会被删除,而其他数据会在过期时根据 LRU 规则进行筛选。
Redis系列:持久化机制Redis系列:数据结构和对象
Loading...