首先,redis数据是在内存中的,这也是它快的重要原因,但是内存中的数据在断电、关机后会被擦除,所以需要复制一份到硬盘,用来做数据恢复,这个从内存复制到硬盘的过程就是其持久化(对的是‘复制’,持久化过的数据还在内存,只有被逐出或者过期才会离开内存)。
redis有AOF和RDB两种持久化方案。
AOF
即只追加日志(append only file),可以设置每秒写入磁盘或者每次写操作都写入磁盘。
具体在redis.conf中配置:
#开启aof
appendonly yes
#always 每次写都刷盘,everysec每秒刷盘,no不主动刷盘,按系统配置。系统通常配置30s一次
appendfsync always
#写入文件名 可以自行设置,如果多个实例要区分开
appendfilename "appendonly.aof"
优点
服务器故障时丢失的数据较少,只有1秒内或者完全不丢失。
缺点
对性能有一定的影响,尤其是每次写操作都写入磁盘时,如果每秒写入一次又会丢失1秒内的数据。并且AOF文件会越来越大,恢复时间没有RDB快。
bgrewriteaof
redis支持bgrewriteaof,它可以以最小体积重写当前AOF文件,比如执行了set name a,set name b,set name c,set name d,set name e,aof重写时就会只写入set name e,重写时已过期的数据不会放入重写文件。
bgrewriteaof是一个bg进程,需要fork()子进程,支持linux的copy-on-write(写时复制),即在fork()出子进程后,父进程继续接受写命令,并把新的写入命令写入一份到当前AOF文件,再写入一份到aof_rewrite_buf,当新的AOF文件生成成功,就替换原有的,也就是整个过程中只有fork()这个动作是同步的,对父进程有影响,接下来就没有影响了。
简化版,哈
redis.conf中相关配置:
注意这里两个条件同时满足才会重写
#AOF自动重写触发文件大小
auto-aof-rewrite-min-size 128mb
#AOF文件增长率 即当新的aof文件比上次压缩后文件增加多少倍时触发重写 比如100 就是比上次重写后的文件大一倍时再次重写
auto-aof-rewrite-percentage 100
#在aof重写期间是否允许不刷盘 yes允许 no 不允许
#aof重写本身就涉及到大量刷盘操作,如果设为no,两者同时刷盘势必对性能有一定影响,但是保证绝不丢数据,如果yes允许不刷,此时redis挂了就可能丢失一部分数据,但是性能较好
no-appendfsync-on-rewrite yes
#aof出错时是否忽略它并启动,如果您有延迟问题,请将此设置为“是”。否则,从耐久性的角度来看,选择“否”是最安全的
aof-load-truncated yes
还涉及到linux的系统设置,/etc/sysctl.conf:
overcommit是linux内存分配里的概念,参考文档有详细介绍这里不详说了,这里推荐设为1,就是在内存不足时依然优先保障来申请的应用(它会根据进程点数来杀死一些正在运行的进程)
#在进行重写时内存分配策略
#默认0 内核检查是否有足够的可用内存供应用进程使用,如果有就允许;否则就申请失败
#建议设为1 内核允许分配所有的物理内存,而不管当前的内存状态如何
#还有一种设置是2 内核允许分配超过所有物理内存和交换空间总和的内存
vm.overcommit_memory 1
AOF追加阻塞
redis会记录和对比上次刷盘时间,如果距离上次同步时间超过两秒,就会阻塞等待同步完成,这是为了避免同步任务失败后一直有新任务再来,为了保证AOF文件安全性,但是主线程阻塞在很多情况下是不允许的。
监测
info aof_delayed_fsync会记录redis阻塞次数,redis日志会记录阻塞问题,linux的top命令也可以查看硬盘状况,总之AOF追加阻塞这种情况是一定要监测和尽量避免的。
RDB
每隔指定周期,fork()出一个子进程执行bgsave,生成当前全部数据的快照文件temp-number.rdb,并在生成完成时替换掉原有rdb文件,这也是linux提供的写时复制技术(copy-on-write)
具体在redis.conf中配置:
#这三种可以同时写,或者再加也可以,满足其中任何一个都会触发rdb,也可以关掉,自己定义重写方式
#每3600s有1次更改就bgsave一次
save 3600 1
#每300s有100次更改就bgsave一次
save 300 100
#每60s有10000次更改就bgsave一次
save 60 10000
#默认文件名 单机多个redis要分开放,二进制文件
dbfilename dump.rdb
#默认路径(这个路径是好几个文件公用的配置)
dir ./
#发生错误是否中断写
stop-writes-on-bgsave-error yes
#是否压缩 压缩对cpu性能要求更高 但是压缩后更省空间,主从间复制时也更快
rdbcompression yes
#是否做校验和
rdbchecksum yes
其他触发RDB的机制
1.主从架构中全量复制时(当从节点增量复制失败会向主节点申请全量复制)
优点
可以将数据恢复到指定时间点,对于灾难恢复非常有用。
结构紧凑,用它重启数据集比AOF更快。
父进程只需要fork()出一个子进程来执行快照,在备份期间父进程工作不受影响。
缺点
会丢失上次保存至今的数据。
虽然bgsave是异步的,并且数据量很大或CPU性能不佳时,fork()过程耗时增加,这会造成主进程在一段时间内暂停接受读写请求。
对比
在同时开始AOF和RDB时,redis优先加载AOF,虽然它比较慢,但是数据比较新。
方案选择
在大部分情况下,我们可以两种方式结合使用,并且配合bgrewriteaof。在集群或哨兵模式下,可以仅在从节点进行备份工作,以减少主节点的压力。
在某些时候,还可以通过手动save来进行RDB备份,它与真正的RDB相比优势在于可以指定执行时间,另外,虽然save会阻塞redis直到快照生成完毕,但是由于它不需要创建子进程并且没有子进程争抢资源,通常比bgsave快很多。
在redis只用来做缓存并且单点访问压力都不大等情况下,可以考虑不做持久化。
优化
fork()优化
aofbgrewrite、bgsave都需要执行fork(),而fork()这个动作是同步的,一旦fork()卡顿对redis性能影响很大
内存占用适当
一般情况下,redis占用内存越大,可存储的key越多,缓存命中率越高,但是内存占用过大fork()占用时间就会增大,maxmemory要适当。
硬件保证
CPU性能低下、可用内存不足,都会影响fork()性能
内存设置保障
vm.overcommit 设为1,可以在内存不足时优先保障申请者,给redis设置较小的点数,使其被kill可能降低
频率限制
综合自己业务和硬件条件,配置适当的持久化方案,不要过于频繁。
性能监测
对硬盘、网络、内存、CPU等做监测,并且监测最近一次fork()的费时(info last_fork_usec),出现异常及时报警
其他优化
多节点架构
在主从架构中,可以只在从节点进行备份,分散压力。
在多主架构中,分片存储可以让每个节点存储数据降低,减少备份压力。
其他
配置生效
设置redis.conf需要重启后才能生效,线上不能重启的情况下,在redis-cli再执行一次config set可以即时生效。
#设置
config set appendonly yes
#查看
config get appendonly
config get 有可能造成慢查询,性能要求极高的场景慎用。不过一般没啥大影响,我本机慢查询日志显示19400(微秒)。
对其他业务的影响
hash原本key达到其一维数组大小时就进行扩容,在bgsave时hash扩容会被暂停,持久化完成继续,当key达到hash一维数组5倍时会强制扩容,缩容不受持久化影响。