Redis内存管理

文章前言

提到内存管理,我们就需要考虑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的配置文件中,我们可以得出,主要分为如下六种淘汰机制。

1
2
3
4
5
6
7
8
# 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.

这六种机制主要是什么意思内,下面是分别针对上面的几种机制做一个说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。

allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)。

volatile-lfu:当内存不足以容纳新写入数据时,在过期密集的键中,使用LFU算法进行删除key。

allkeys-lfu:当内存不足以容纳新写入数据时,使用LFU算法移除所有的key。

volatile-random:当内存不足以容纳新写入数据时,在设置了过期的键中,随机删除一个key。

allkeys-random:当内存不足以容纳新写入数据时,随机删除一个或者多个key。

volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。

内存管理配置翻译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# 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,则该返回值则为过期的时间;