文章前言
提到内存管理,我们就需要考虑 Redis 的内存过期策略和内存淘汰机制。该文章便从这两方面入手,分享一些在 Redis 内存方面相关的基础知识。
文章中使用的示例版本为 Redis5.0 版本。
内存过期策略
内存过期策略主要的作用就是,在缓存过期之后,能够及时的将失效的缓存从内存中删除,以减少内存的无效暂用,达到释放内存的目的。
过期策略分类
Redis 内存过期策略分为三类,定时策略、惰性策略和定期策略。
定时策略
含义:在设置 key 的过期时间的同时,为该 key 创建一个定时器,让定时器在 key 的过期时间来临时,对 key 进行删除。
优点:保证内存被尽快释放,减少无效的缓存暂用内存。
缺点:若过期 key 很多,删除这些 key 会占用很多的 CPU 时间,在 CPU 时间紧张的情况下,CPU 不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些 key。定时器的创建耗时,若为每一个设置过期时间的 key 创建一个定时器(将会有大量的定时器产生),性能影响严重。一般来说,是不会选择该策略模式。
惰性策略
含义:key 过期的时候不删除,每次从数据库获取 key 的时候去检查是否过期,若过期,则删除,返回 null。
优点:删除操作只发生在从数据库取出 key 的时候发生,而且只删除当前 key,所以对 CPU 时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的 key 了)。
缺点:若大量的 key 在超出超时时间后,很久一段时间内,都没有被获取过,此时的无效缓存是永久暂用在内存中的,那么可能发生内存泄露(无用的垃圾占用了大量的内存)。
定期策略
含义:每隔一段时间对设置了缓存时间的 key 进行检测,如果可以已经失效,则从内存中删除,如果未失效,则不作任何处理。
优点:通过限制删除操作的时长和频率,来减少删除操作对 CPU 时间的占用 – 处理 ” 定时删除 ” 的缺点
定期删除过期 key– 处理 ” 惰性删除 ” 的缺点。
缺点: 在内存友好方面,不如 ” 定时删除 ”,因为是随机遍历一些 key,因此存在部分 key 过期,但遍历 key 时,没有被遍历到,过期的 key 仍在内存中。在 CPU 时间友好方面,不如 ” 惰性删除 ”,定期删除也会暂用 CPU 性能消耗。
难点: 合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)
该方式不是去便利所有的 ky,而是随机抽取一些 key 做过期检测。
策略注意事项
过期策略对持久化存储的影响
持久化存储,指的是将内存的缓存永久存在磁盘中。也就是说我们的 AOF 和 RDB 持久化存储方式。因为该两种方式,将内存中的数据写入磁盘,这时候就需要考虑到我们过期的缓存是否会被写入到磁盘中?如果写入磁盘又是怎么处理的?
RDB 持久化
持久化 key 之前,会检查是否过期,过期的 key 不进入 RDB 文件。
数据载入数据库之前,会对 key 先进行过期检查,如果过期,不导入数据库(主库情况)。
AOF 持久化
当 key 过期后,还没有被删除,此时进行执行持久化操作(该 key 是不会进入 aof 文件的,因为没有发生修改命令)。
当 key 过期后,在发生删除操作时,程序会向 aof 文件追加一条 del 命令(在将来的以 aof 文件恢复数据的时候该过期的键就会被删掉)。
因为 AOF 方式,向存储文件追加的是 Redis 的操作命令,而不是具体的数据,然而 RDB 确是存储的安全的二进制内容。
重写时,会先判断 key 是否过期,已过期的 key 不会重写到 aof 文件。
即使在重写时,不验证是否过期,然而追加了 del 命令,测试无效的 key 同样会被删除。判断的情况是为了防止没有加入 del 命令的 key。
内存淘汰机制
定义说明
内存淘汰机制针对是内存不足的情况下的一种 Redis 处理机制。例如,当前的 Redis 存储已经超过内存限制了,然而我们的业务还在继续往 Redis 里面追加缓存内容,这时候 Redis 的淘汰机制就起到作用了。
淘汰机制分类
根据 redis.conf 的配置文件中,我们可以得出,主要分为如下六种淘汰机制。
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with 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.
这六种机制主要是什么意思内,下面是分别针对上面的几种机制做一个说明。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key。allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。volatile-lfu:当内存不足以容纳新写入数据时,在过期密集的键中,使用 LFU 算法进行删除 key。allkeys-lfu:当内存不足以容纳新写入数据时,使用 LFU 算法移除所有的 key。volatile-random:当内存不足以容纳新写入数据时,在设置了过期的键中,随机删除一个 key。allkeys-random:当内存不足以容纳新写入数据时,随机删除一个或者多个 key。volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
内存管理配置翻译
# Set a memory usage limit to the specified amount of bytes.
#将内存使用限制设置为指定的字节数。# When the memory limit is reached Redis will try to remove keys
#当达到内存限制时,Redis 将尝试删除密钥
# according to the eviction policy selected (see maxmemory-policy).
#根据所选的逐出策略(请参阅 maxmemory 策略)。#
#
# If Redis can't remove keys according to the policy, or if the policy is
#如果 Redis 不能根据策略删除密钥,或者如果策略是
# set to 'noeviction', Redis will start to reply with errors to commands
#设置为“noeviction”时,Redis 将开始对命令进行错误的应答
# that would use more memory, like SET, LPUSH, and so on, and will continue
#这将使用更多内存,如 SET、LPUSH 等,并将继续
# to reply to read-only commands like GET.
#回复像 GET 这样的只读命令。#
#
# This option is usually useful when using Redis as an LRU or LFU cache, or to
#当将 Redis 用作 LRU 或 LFU 缓存或
# set a hard memory limit for an instance (using the 'noeviction' policy).
#为实例设置硬内存限制(使用“noeviction”策略)。#
#
# WARNING: If you have replicas attached to an instance with maxmemory on,
#警告:如果将副本附加到启用了 maxmemory 的实例,# the size of the output buffers needed to feed the replicas are subtracted
#减去为复制副本提供数据所需的输出缓冲区的大小
# from the used memory count, so that network problems / resyncs will
#从网络中重新同步使用的问题
# not trigger a loop where keys are evicted, and in turn the output
#不触发一个循环,其中键被逐出,而反过来输出
# buffer of replicas is full with DELs of keys evicted triggering the deletion
#复制副本的缓冲区已满,退出的密钥将触发删除
# of more keys, and so forth until the database is completely emptied.
#直到数据库完全清空。#
# In short... if you have replicas attached it is suggested that you set a lower
#简而言之。。。如果您附加了副本,建议您设置较低的
# limit for maxmemory so that there is some free RAM on the system for replica
#限制 maxmemory,以便系统上有一些可用的 RAM 用于复制
# output buffers (but this is not needed if the policy is 'noeviction').
#输出缓冲区(但如果策略为“noeviction”,则不需要此缓冲区)。# maxmemory <bytes>
#最大内存 < 字节 >
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
#MAXMEMORY 策略:当 MAXMEMORY 时 Redis 如何选择要删除的内容
# is reached. You can select among five behaviors:
#已到达。您可以从五种行为中进行选择:#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
#volatile lru-> 在具有 expire 集的密钥中使用近似的 lru 进行逐出。# allkeys-lru -> Evict any key using approximated LRU.
#allkeys lru-> 使用近似的 lru 逐出任何键。# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
#volatile lfu-> 在具有 expire 集的密钥中使用近似的 lfu 进行逐出。# allkeys-lfu -> Evict any key using approximated LFU.
#allkeys lfu-> 使用近似的 lfu 逐出任何键。# volatile-random -> Remove a random key among the ones with an expire set.
#volatile random-> 从具有 expire 集的密钥中删除一个随机密钥。# allkeys-random -> Remove a random key, any key.
#allkeys random-> 移除一个随机键,任意键。# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
#volatile ttl-> 删除最接近过期时间的密钥(minor ttl)# noeviction -> Don't evict anything, just return an error on write operations.
#noeviction-> 不要逐出任何内容,只要在写操作时返回一个错误。# LRU means Least Recently Used
#LRU 表示最近最少使用
# LFU means Least Frequently Used
#LFU 表示使用频率最低
# Both LRU, LFU and volatile-ttl are implemented using approximated
#LRU、LFU 和 volatile ttl 都是用近似方法实现的
# randomized algorithms.
#随机算法。# Note: with any of the above policies, Redis will return an error on write
#注意:如果使用上述任何策略,Redis 将在写入时返回错误
# operations, when there are no suitable keys for eviction.
#操作,当没有合适的钥匙驱逐。#
#At the date of writing these commands are: set setnx setex append
#在编写这些命令的日期是:set setnx setex append
#incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#增加 / 减少脉冲低压脉冲 rpushx lpushx linsert lset RPOPPLPUSH sadd
#sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#sinter sinterstore sunion sunionstore sdiffstore zadd zincrby
#zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#zunionstore zinterstore hset hsetnx hmset hincrby incrby 递减
#getset mset msetnx exec sort
#getset mset msetnx exec 排序
#
#
# The default is:
#默认值为:#
#
# maxmemory-policy noeviction
#maxmemory 策略不可用
# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
#LRU、LFU 和最小 TTL 算法不是精确算法而是近似算法
# algorithms (in order to save memory), so you can tune it for speed or
#算法(为了节省内存),所以你可以调整它的速度或
# accuracy. For default Redis will check five keys and pick the one that was
#准确度。对于默认的 Redis 将检查五个键并选择一个
# used less recently, you can change the sample size using the following
#最近使用较少,可以使用以下命令更改样本大小
# configuration directive.
#配置指令。#
#
# The default of 5 produces good enough results. 10 Approximates very closely
#默认值为 5 会产生足够好的结果。10 非常接近
# true LRU but costs more CPU. 3 is faster but not very accurate.
#真正的外场可更换单元,但占用更多的 CPU。3 更快,但不是很准确。# maxmemory-samples 5
#maxmemory 示例 5
# Starting from Redis 5, by default a replica will ignore its maxmemory setting
#从 Redis 5 开始,默认情况下副本将忽略其 maxmemory 设置
# (unless it is promoted to master after a failover or manually). It means
#(除非在故障转移后或手动升级为主节点)。意思是
# that the eviction of keys will be just handled by the master, sending the
#密钥的收回将由主机处理,发送
# DEL commands to the replica as keys evict in the master side.
#DEL 命令在主服务器端作为密钥收回。#
# This behavior ensures that masters and replicas stay consistent, and is usually
#这种行为可以确保主机和副本保持一致,并且通常
# what you want, however if your replica is writable, or you want the replica to have
#但是,如果您的复制副本是可写的,或者您希望复制副本具有
# a different memory setting, and you are sure all the writes performed to the
#不同的内存设置,并且您确定对
# replica are idempotent, then you may change this default (but be sure to understand
#复制副本是幂等的,那么您可以更改这个默认值(但是一定要理解
# what you are doing).
#你在做什么)。#
# Note that since the replica by default does not evict, it may end using more
#请注意,由于复制副本在默认情况下不会逐出,它可能会使用更多
# memory than the one set via maxmemory (there are certain buffers that may
#内存大于通过 maxmemory 设置的内存(有某些缓冲区可能
# be larger on the replica, or data structures may sometimes take more memory and so
#复制副本越大,或者数据结构有时可能占用更多内存,因此
# forth). So make sure you monitor your replicas and make sure they have enough
#第四)。所以一定要监控你的复制品,确保它们有足够的
# memory to never hit a real out-of-memory condition before the master hits
#内存在主机命中之前永远不会遇到内存不足的情况
# the configured maxmemory setting.
#配置的 maxmemory 设置。#
#
# replica-ignore-maxmemory yes
#复制副本忽略 maxmemory 是
Redis 命令
这里总结几个 Redis 中常用的与时间有关的命令。
exists key: 判断键是否存在,如果存在则返回 1,不存在则返回 0;
expire key: 给键设置过期时间,单位 s(秒);
ttl key: 返回键剩余的过期时间,单位 s(秒);当键不存在是返回 -2;存在并且未设置过期时间,返回 -1;如果返回≥0,则该返回值则为过期的时间;
ptt key: 返回键剩余的过期时间,单位 ms(毫秒);当键不存在是返回 -2;存在并且未设置过期时间,返回 -1;如果返回≥0,则该返回值则为过期的时间;