redis内存回收机制&持久化
Redis的内存回收机制
我们知道redis是基于内存的,而内存非常珍贵的资源;不可能所有的数据都放在内存中。因此redis也会存在内存回收机制。
redis内存回收有两种场景
- 删除过期时间的key值;
- 内存使用达到了设定的maxmemeory上限时触发内存淘汰策略
Redis的过期策略
redis的过期策略
- 定时过期:对设置过期时间的key创建一个定时器,到过期时间自动删除。但是因为要占用CPU的资源,所以会对响缓存的响应时间和吞吐量有一定的影响
- 惰性过期:当访问一个key时才去判断这个key是否已经过期,若过期则进行清除。这种策略节省了CPU资源,但是会占用内存
- 定时扫描:每隔一段时间,redis会开启一个线程去扫描若干数据库中的expires字典中一定数量的key,并清除其中已经过期的key。该策略是前两种策略的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定时耗,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
Redis中同时使用了惰性删除和定时扫描这两种过期策略
惰性删除
在进行get或者set/setex/setnx等操作时,先检测key是否过期;过期,则删除key,然后执行相应操作;没过期则直接执行对应命令
定时扫描
循环遍历redis中的每一个库(默认16个,序号从0开始),检查库中是指定数量的key,如果库中没有设置过期事件的key就直接进入下一个库。所及获取设置了过期事件的key检查是都有过期,过期则删除。定时扫描操作是否已经达到指定时长,若已经达到,直接退出定期删除;
对于定时扫描删除,在redis中有一个全局变量current_db来记录下一个将要遍历的库。假设现在有16个库,我们这一次定时扫描遍历了6个库,那么此时的current_db就是6,下一次定时扫描将直接从第6个库开始遍历。
Redis的淘汰策略
当内存达到设定的maxmemory上限时,redis开启内存淘汰策略以清除旧数据保证新数据的存入。Redis默认使用近似LRU算法来进行内存淘汰。即最近最少使用。可以查看 redis.conf来查看。
配置LRU:
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or accuracy.
# LRU和最小TTL算法不是精确算法,而是近似算法(为了节省内存),可以来配置和调正来获得满意的速度和精度
# For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.
# 默认情况下,Redis将检查五个键并选择最近使用的键,可以使用以下配置指令更改样本大小。
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.
# 默认值为5会产生足够好的结果。10非常接近真实的LRU,但需要更多的CPU。3很快,但不是很准确
# maxmemory-samples 5
配置最大内存使用:
# 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 <bytes>
对于64 bit的机器,如果maxmemory设置为0,那么就默认不限制内存的使用,直到耗尽机器中所有的内存为止;,但是对于32 bit的机器,有一个隐式的闲置就是3GB。
淘汰策略:
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
# 当内存不足以容纳新写入的数据时
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# 在设置了过期时间的键中使用LRU算法移除最近最少使用的键;
# allkeys-lru -> remove any key according to the LRU algorithm
# 使用LRU算法移除最近最少使用的key;
# volatile-random -> remove a random key with an expire set
# 在设置了过期时间的键中随机移除某个键;
# allkeys-random -> remove a random key, any key
# 随机删除key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# 在设置了过期时间的键中,越接近过期的key将被有限移除;
# noeviction -> don't expire at all, just return an error on write operations
# 新写入操作会直接报错;
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
# 默认 noeviction 即 新写入操作会直接报错;
# maxmemory-policy noeviction
Redis的持久化
Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis的持久化有2种
- RDB(二进制快照,压缩的二进制文件)
- aof(类似于bin_log)
RDB(SNAPSHOTTING)
触发rdb分为2种方式
- 手动触发
save命令和bgsave。
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。
- 自动触发
redis.conf中配置
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
# save time count
# 在 time秒内至少发生 count 次数据变化,则会触发。多个配置取合集
save 900 1
save 300 10
save 60 10000
rdb常见的配置
# 如果900s内,如果至少有一个1个key进行了修改,就进行持久化操作
save 900 1
# 如果300s内,如果至少10个key进行了修改,就进行持久化操作
save 300 10
# 如果60s内,如果至少10000个key进行了修改,就进行持久化操作
save 60 10000
# 关闭该规则使用save “ ”
# yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件。
stop-writes-on-bgsave-error yes
# 是否压缩 rdb 文件,需要消耗一些cpu资源,该功能可以节约磁盘空间。
rdbcompression yes
# 在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动
rdbchecksum yes
# 持久化文件名
dbfilename dump.rdb
# 数据文件存放目录,rdb快照文件和aof文件都会存放至该目录,请确保有写权限
dir ./
流程图
- Redis父进程收到命令 bgsave
- 判断是否已经有子进程在执行,如果有则bgsave命令直接返回
- 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
- 父进程fork后,bgsave命令返回Background saving started信息并不再阻塞父进程,并可以响应客户端其他命令
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换
- 子进程发送信号给父进程表示完成,父进程更新相关的统计信息
rdb触发场景
- 手动执行命令save和bgsave会生成快照;
- 配置文件save time count 规则进行自动快照;
- 主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave命令进行快照;
- 执行数据库清空命令FLUSHALL
- 执行shutdown关闭redis
AOF(APPEND ONLY MODE)
Redis服务器默认开启RDB,关闭AOF;
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
# 上面那一串英文意思就是可以同时开启 AOP和RDB
# Please check http://redis.io/topics/persistence for more information.
# yes 表示开启、no表示关闭.
appendonly yes
aof会记录Redis的每条写命令,因此AOF不需要触发。在redis启动时候优先选择从AOF文件恢复数据。
# 查看 aof配置命令
config get appendonly
1) "appendonly"
2) "no"
AOF执行流程
- 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
- 文件写入和文件同步(write&sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
- 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。
文件重写是指定期重写AOF文件,减小AOF文件的体积。AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作。
- 触发AOF时会将命令写入到缓冲区aof_buf,当aof_buf被填满或超过了指定时限后将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失;因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
- 随着时间流逝,Redis服务器执行的写命令越来越多,AOF文件也会越来越大,触发了AOF的重写(或者通过命令bgrewriteaof手动触发),此时缓存区为aof_rewirte_buf。把AOF重写缓冲区的数据写入到新的AOF文件。然后 使用新的AOF文件替换老文件,完成AOF重写。
AOF文件重写为什么会减少aof文件大小
- 已经过期的数据不再需要写入文件
- 无效的命令不再写入文件,比如set name 非鸽传书 和 set name 关注点赞转发 或者 sadd action 关注点赞转发, del action等等。
aof的一些配置
# 默认是不开启aof模式的,默认使用rdb方式进行持久化,大部分情况rdb够用
appendonly no
# 每执行一次更新命令,持久化一次
appendfsync always
# 每秒钟持久化一次
# everysec是上面两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。
appendfsync everysec
# 不持久化
appendfsync no
# 只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时满足时,才会自动触发AOF重写
# 执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。
auto-aof-rewrite-percentage 100
# 执行AOF重写时,文件的最小体积,默认值为64MB
auto-aof-rewrite-min-size 64mb
# 如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件
aof-load-truncated yes
# AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡
no-appendfsync-on-rewrite no
上面提到过redis启动时候优先选择从AOF文件恢复数据,一旦这个aof文件有误, redis 是启动不起来的。修复这个aof文件 redis 给我们提供了一个工具 redis-check-aof --fix 。
AOF和RDB对比
- RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。
- RDB相对AOF对性能的影响相对较小。
- AOF支持秒级持久化、兼容性好。但是恢复速度相对慢,对性能影响比较大
持久化策略
首先我们要清楚一点就是开启redis的持久化对redis的性能是有一定的影响的。RDB会在fork的时候短暂的阻塞,AOF也是频繁的访问磁盘会增加服务器的IO压力。在实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略。而且持久化的选择必须与Redis的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机master和从机slave可以独立的选择持久化方案。
下面进行分场景讨论,也只能是简单的参考,实际方案应该复杂的多。
1、redis的数据不重要,可有可无,对系统本身影响不大。那这种情况完全可以放弃持久化。
2、如果能接受一段时间内的数据丢失可以考虑仅使用RDB
3、主从环境,slave实现数据的热备和进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。
master可以关闭持久化,slave视情况(数据丢失影响是否很大)来决定是开启RDB还是AOF。如果是开启AOF可以关闭AOF的自动重写,使用linux的定时任务在业务发生量小时进行aof文件的重写。
4、异地灾备,异地灾备往往是用来应对地震火灾等等意外场景(换句话说就是集群全部牺牲了,硬件也可能损坏的情况)。这种情况应用一般也要求尽快恢复使用,因此可以采用恢复较快的rdb方式来备份(AOF也是可以)。每天定时将RDB文件上云或者远端传输至异地服务。异地备份的频率根据数据安全性的需要及其他条件来确定,但最好不要低于一天一次。
封面图,侵权删(这次应该没水印了。我知道进来都是看图的,但是还是不要脸的求关注、收藏、点赞)