type
status
date
slug
summary
tags
category
password
1、Redis键空间
默认情况下,Redis 中有 16 个数据库,编号从 0-15。每个 Redis 数据库使用一个 redisDb 对象来表示,每个 redisDb 对象实际上是一个 dict 结构的数据集合。
其中 dict 保存了该 db 中的所有键值对。键值对中键是一个字符串对象,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象在内的任意一种 RedisObject。

redis 通过全局哈希表保存所有的 key 值,所以查找 key 的时间为 O(1)。redis 一下次写入太多数据有可能会产生哈希冲突,全局哈希表也是通过渐进式 rehash 来解决 hash 冲突的问题。
2、过期删除策略
过期删除策略就是指 key 过期后 Redis 会如何删除。我们可以设置 Redis 中缓存的 key 的过期时间。通常有以下策略:
过期策略 | 描述 | 优点 | 缺点 |
定时删除 | 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。 | 节省内存 | CPU不友好,频繁的删除可能会影响性能 |
惰性删除 | 访问一个key时,会先判断该key是否已过期,过期则清除。 | 节省CPU资源,只在访问时进行过期检查,不会因为删除大量过期键而影响系统性能 | 如果键长期不被访问,即使已过期也会占用内存,导致内存浪费 |
定期删除 | 定期随机检查一批设置了过期时间的key,删除其中已过期的key | 前两者的一个折中方案,使得CPU和内存资源达到最优的平衡效果。 | 需要找到合适的执行频率平衡点 |
Redis同时使用了惰性过期和定期过期两种过期策略:
- 每当我们设置一个 key 的过期时间时,Redis 就会将该 key 带上过期时间存放到一个过期 dict 中。
- 当我们查询一个键时,Redis 便首先检查该 key 是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。
- Redis 默认会每 100ms 扫描一次过期 dict,但不会遍历过期 dict 中所有的 key,而是采用了一种简单的贪心策略。
- 从过期字典中随机选 20 个 key。
- 删除这 20 个 key 中已经过期的 key。
- 如果过期的 key 比率超过 1/4,那就重复步骤 1。
3、内存淘汰策略
内存淘汰策略是指在内存不足时如何删除 key 来释放空间。虽然我们有过期策略来淘汰已过期的数据,但是如果大量数据过期时间还没到,但此时内存已经快被占满了,我们就需要通过内存淘汰策略选择淘汰哪些数据。
内存淘汰策略有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。

内存淘汰策略 | 描述 | 适用场景 |
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 规则进行筛选。
- Author:mcbilla
- URL:http://mcbilla.com/article/f073ccca-28a3-4492-9cd8-05380ac83f4a
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!