【Redis系列笔记】内存淘汰及过期删除

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis是一个内存键值对数据库,所以对于内存的管理尤为重要。Redis内部对于内存的管理主要包含两个方向,过期删除策略和数据淘汰策略。内存淘汰策略指在Redis内存使用达到一定阈值的时候,执行某种策略释放内存空间,以便于接收新的数据。数据过期删除策略是指在数据的有效时间到期后,如何从内存中删除这些数据的规则。

Redis是一个内存键值对数据库,所以对于内存的管理尤为重要。Redis内部对于内存的管理主要包含两个方向,过期删除策略和数据淘汰策略。

1. 内存淘汰策略

1.1. 概述

内存淘汰策略指在Redis内存使用达到一定阈值的时候,执行某种策略释放内存空间,以便于接收新的数据。内存可使用空间由配置参数maxmemory决定(单位mb/GB)。故又叫"最大内存删除策略",也叫"缓存删除策略"。

1.2. 最大内存

# 客户端命令方式配置和查看内存大小
127.0.0.1:6379> config get maxmemory
"maxmemory"
"0"
127.0.0.1:6379> config set maxmemory 100mb
OK
127.0.0.1:6379> config get maxmemory
"maxmemory"
"104857600"
# 配置文件配置maxmemory大小
# WARNING: not setting maxmemory will cause Redis to terminate with an
# out-of-memory exception if the heap limit is reached.
#
# NOTE: since Redis uses the system paging file to allocate the heap memory,
# the Working Set memory usage showed by the Windows Task Manager or by other
# tools such as ProcessExplorer will not always be accurate. For example, right
# after a background save of the RDB or the AOF files, the working set value
# may drop significantly. In order to check the correct amount of memory used
# by the redis-server to store the data, use the INFO client command. The INFO
# command shows only the memory used to store the redis data, not the extra
# memory used by the Windows process for its own requirements. Th3 extra amount
# of memory not reported by the INFO command can be calculated subtracting the
# Peak Working Set reported by the Windows Task Manager and the used_memory_peak
# reported by the INFO command.
#
maxmemory 100mb
#...

若maxmemory=0则表示不做内存限制,但是对于windows系统来说,32位系统默认可使用空间是3G,因为整个系统内存是4G,需要留1G给系统运行。且淘汰策略会自动设置为noeviction,即不开启淘汰策略,当使用空间达到3G的时候,新的内存请求会报错。

1.3. 分类

淘汰策略配置maxmemory-policy,表示当内存达到maxmemory时,将执行配置的淘汰策略,由redis.c/freeMemoryIfNeeded 函数实现数据淘汰逻辑。

在redis文件目录下redis.conf中,可看到提供8种淘汰策略,在3.0之前提供其中6种,4.0以上版本增加了两种LFU策略。

#redis.conf文件配置方式
# 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.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU, only keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key 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 means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
# The default is:
# ...
maxmemory-policy noeviction

数据淘汰策略

描述

noeviction

不删除任何数据,当内存不足时直接报错

volatile-lru

挑选最近最久未使用的数据进行淘汰

volatile-lfu

挑选最近最少使用的数据进行淘汰

volatile-ttl

挑选将要过期的数据进行淘汰

volatile-random

随机选择数据进行淘汰

allkeys-lru

挑选最近最久未使用的数据进行淘汰,包括所有数据

allkeys-lfu

挑选最近使用次数最少的数据进行淘汰,包括所有数据

allkeys-random

随机选择数据进行淘汰,包括所有数据

  • LRULeast Recently Used)最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。数据越久没被访问,优先淘汰。
  • LFULeast Frequently Used)最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。数据访问频次越少,优先淘汰。
  • volatile:表示设置了带过期时间的key
  • allkeys:表示所有的key
# 命令行配置方式
127.0.0.1:6379> CONFIG GET maxmemory-policy
"maxmemory-policy"
"noeviction"
127.0.0.1:6379> CONFIG SET maxmemory-policy volatile-lru
OK
127.0.0.1:6379> CONFIG GET maxmemory-policy
"maxmemory-policy"
"volatile-lru"

1.4. 使用建议

  1. 优先使用 allkeys-lru 策略。充分利用 LRU 算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
  2. 如果业务中有置顶的需求,可以使用 volatile-lru 策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
  3. 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用 allkeys-random 或 volatile-random,随机选择淘汰。
  4. 如果业务中有短时高频访问的数据,可以使用 allkeys-lfu 或 volatile-lfu 策略。

1.5. 总结

Redis在实现淘汰策略时为了更合理的利用内存空间以及保证Redis的高性能,只是几近于算法的实现机制,其会从性能和可靠性层面做出一些平衡,故并不是完全可靠的。因此我们在实际使用过程中,建议都配置过期时间,主动删除那些不再使用的数据,以保证内存的高效使用。另外关于LRU和LFU算法,Redis内部在数据结构和实现机制上都做了一定程度的适应性改造

2. 过期删除策略

2.1. 概述

Redis中的数据过期删除策略是指在数据的有效时间到期后,如何从内存中删除这些数据的规则。

2.2. 常见的删除方式

  • 定时删除:在写入key之后,根据否配置过期时间生成特定的定时器,定时器的执行时间就是具体的过期时间。用CPU性能换去内存存储空间——即用时间获取空间。
  • 定期删除:提供一个固定频率的定时器,执行时扫描所有的key进行过期检查,满足条件的就进行删除。
  • 惰性删除:数据不做及时释放,待下一次接收到读写请求时,先进行过期检查,若已过期则直接删除。用内存存储空间换取CPU性能——即用空间换取时间。

删除方式

优点

缺点

定时删除

能及时释放内存空间,不会产生滞留数据

频繁生成和销毁定时器,非常损耗CPU性能,影响响应时间和指令吞吐量

定期删除

固定频率进行过期检查,对CPU友好

1. 数据量大时,全局扫描损耗CPU性能,主线程阻塞导致其他请求响应延迟

2. 未能及时释放内存空间

3. 数据不一致

惰性删除

节约CPU性能

当某些数据长时间无请求访问时,会导致数据滞留,使内存无法释放,占用内存空间,甚至引发内存泄漏导致服务不可用

Redis中结合了定期删除与惰性删除两种方式来完成。下面针对两者展开描述。

2.3. 惰性删除

概述

惰性删除指的是设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。

特点

  • 优点 :对CPU友好,对于很多用不到的key不用浪费资源进行过期检查。
  • 缺点 :对内存不友好,key过期不被使用,就不会被清理就会一直占用内存。

2.4. 定期删除

概述

每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键)。

特点

  • 优点:相对于定时删除,可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除弥补惰性删除的缺点,能有效释放过期键占用的内存。
  • 缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。

模式

  • SLOW模式是定时任务,执行频率默认为10hz,执行间隔为100ms,执行时间不超过25ms。可通过修改配置文件redis.conf 的 hz 选项来调整这个次数。
  • FAST模式执行频率不固定,每次事件循环会尝试执行,但两次间隔不低于2ms,每次耗时不超过1ms

这两种模式规定执行间隔以及执行时间,为了不影响主线程的执行。

2.5. 底层原理

当key设置了过期时间,Redis内部会将这个key带上过期时间放入过期字典(expires)中,当进行查询时,会先在过期字典中查询是否存在该键,若存在则与当前UNIX时间戳做对比来进行过期时间判定。

过期时间配置命令如下(即EX|PX|EXAT|PXAT):

# expire: t秒后过期
expire key seconds
# pexpire: t毫秒后过期
pexpire key millseconds
# expireat: 到达具体的时间戳时过期,精确到秒
expireat key timestamp
# pexpireat: 到达具体的时间戳时过期,精确到毫秒
pexpire key millseconds

这四个命令看似有差异,但在RedisDb底层,最终都会转换成pexpireat指令。内部由db.c/expireGenericCommand函数实现,对外由上面四个指令调用

//expire命令
void expireCommand(redisClient *c) {
  expireGenericCommand(c,mstime(),UNIT_SECONDS);
}
//expireat命令
void expireatCommand(redisClient *c) {
  expireGenericCommand(c,0,UNIT_SECONDS);
}
//pexpire命令
void pexpireCommand(redisClient *c) {
  expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
}
//pexpireat命令
void pexpireatCommand(redisClient *c) {
  expireGenericCommand(c,0,UNIT_MILLISECONDS);
}
/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT
* and PEXPIREAT. Because the commad second argument may be relative or absolute
* the "basetime" argument is used to signal what the base time is (either 0
* for *AT variants of the command, or the current time for relative expires).
*/
void expireGenericCommand(redisClient *c, long long basetime, int unit) {
  ...
  /* unix time in milliseconds when the key will expire. */
  long long when; 
  ...
  //如果是秒转换为毫秒
  if (unit == UNIT_SECONDS) when *= 1000;
  when += basetime;
  ...
}
  • 过期字典内部存储结构:key表示一个指向具体键的指针,value是long类型的毫秒精度的UNIX时间戳。
  • Rediskey过期时间内部流程图:

2.6. 脏读现象

在单节点实例模式下,因为Redis是单线程模型,所以过期策略可以保证数据一致性。

在集群模式下,过期删除策略会引起脏读现象

  • 数据的删除在主库执行,从库不会执行。对于惰性删除策略来说,3.2版本以前,从库读取数据时哪怕数据已过期还是会返回数据,3.2版本以后,则会返回空。
  • 对于定期删除策略,由于只是随机抽取了一定的数据,此时已过期但未被命中删除的数据在从库中读取会出现脏读现象。
  • 过期时间命令EX|PX,在主从同步时,因为同步需要时间,就会导致主从库实际过期时间出现偏差。比如主库设置过期时间60s,但同步全量花费了1分钟,那么在从库接收到命令并执行之后,就导致从库key的过期时间整体跨越了两分钟,而此时主库在一分钟之前数据就已经过期了。EXAT|PXAT 命令来设置过期时间节点。这样可避免增量同步的发生。但需注意主从服务器时间一致。

2.7. 总结

Redis中惰性删除和定期删除这两种方式的结合,能很好的解决过期数据滞留内存的问题,同时也很好的保证了数据的一致性,保证了内存使用的高效与CPU的性能。

但在实际使用过程中,过期时间配置只是一种常规手段,当key的数量在短时间内突增,就有可能导致内存不够用。此时就需要依赖于Redis内部提供的淘汰策略来进一步的保证服务的可用性。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
26天前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
24 2
|
14天前
|
NoSQL 算法 Redis
redis内存淘汰策略
Redis支持8种内存淘汰策略,包括noeviction、volatile-ttl、allkeys-random、volatile-random、allkeys-lru、volatile-lru、allkeys-lfu和volatile-lfu。这些策略分别针对所有键或仅设置TTL的键,采用随机、LRU(最近最久未使用)或LFU(最少频率使用)等算法进行淘汰。
32 5
|
2月前
|
存储 缓存 NoSQL
Redis Quicklist 竟让内存占用狂降50%?
【10月更文挑战第11天】
52 2
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
59 0
|
3月前
|
缓存 NoSQL 算法
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
14)Redis 在内存用完时会怎么办?如何处理已过期的数据?
71 0
|
3月前
|
存储 缓存 NoSQL
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
82 0
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
78 6
|
1月前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题

相关产品

  • 云数据库 Tair(兼容 Redis)