Redis持久化RDB和AOF测试


参考文档:
https://blog.csdn.net/weixin_40074110/article/details/125519332
https://www.cnblogs.com/zyf98/p/15934058.html

RDB:

save 900 1 
save 300 10 
save 60 10000 
stop-writes-on-bgsave-error yes 
rdbcompression yes  
rdbchecksum yes     
dbfilename "dump_6379.rdb"  
rename-command FLUSHDB ""
rename-command FLUSHALL ""

1.触发rdb持久化的四种条件:
1.执行save命令;save命令会导致主进程执行RDB,这个过程中其他所有命令都会被阻塞的。只有数据迁移时候,可能会用到这个命令。其他时候,慎用啊
2.执行bgsave命令;这个命令执行后会开启独立的进程完成RDB,主进程可以持续处理用户的请求,主进程不受影响。
3.Redis停机时候;Redis停机的时候(shutdown),会执行一次save命令,实现RDB持久化。
4.触发RDB条件时候。save 60 10000,save 300 10,save 900 1分别是触发RDB持久化的三个条件((新增、修改和删除,注意对同一Key的多次修改,修改多少次就算多少次),三个条件是独立的
,save 60 10000为例,是指的超过60S后开始,积累的dirty key大于等于10000时触发持久化。触发任何一个都会进行bgsave操作,执行bgsave命令时,Redis主进程会fork一个子进程来完成RDB的过程,会
先将数据写入到一个临时二进制文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件(可以理解为Copy On Write机制)。

2.save m n 配置的实现原理:
通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。Redis会每隔100ms执行serverCron函数,会检查所有save m n的配置,只要满足一个save m n则执行bgsave。
当前时间-lastsave > m
dirty >= n

3.RDB持久化原理
最近生产环境中某个Set的Redis集群经常出现短暂的内存降低现象,经过查看日志是因为在RDB持久化所造成的内存突降(日志中:RDB: 4929 MB of memory used by copy-on-write  ),
其根本原理是RDB持久化的过程中,Redis借助操作系统提供的写时复制技术(Copy-On-Write,COW),在执行bgsave(snapshot)快照的同时,会间接消耗额外的内存。
  RDB是一次的全量备份,即周期性的把Redis当前内存中的全量数据写入到一个快照文件中。Redis是单线程程序,这个线程要同时负责多个客户端的读写请求,
  还要负责周期性的把当前内存中的数据写到快照文件中RDB中,数据写到RDB文件是IO操作,IO操作会严重影响Redis的性能,甚至在持久化的过程中,读写请求会阻塞,
  为了解决这些问题,Redis需要同时进行读写请求和持久化操作,这样又会导致另外的问题:持久化的过程中,内存中的数据还在改变,假如Redis正在进行持久化一个大的数据结构,
  在这个过程中客户端发送一个删除请求,把这个大的数据结构删掉了,这时候持久化的动作还没有完成,那么Redis该怎么办呢?
  于是Redis使用操作系统的多进程写时复制(Copy On Write)机制来实现快照的持久化,在持久化过程中调用glibc(Linux下的C函数库)的函数fork()产生一个子进程,快照持久化完全交给子进程来处理,
  父进程继续处理客户端的读写请求。子进程刚刚产生时,和父进程共享内存里面的代码段和数据段,也就是说,父子进程的虚拟空间不同,但其对应的物理空间(内存区)是同一个。
  这是Linux操作系统的机制,为了节约内存资源,所以尽可能让父子进程共享内存,这样在进程分离的一瞬间,内存的增长几乎没有明显变化。
  写时复制技术:
       如果主线程收到的客户端的读写请求,需要修改某块数据,那么这块数据就会被复制一份到内存,生成该数据的副本,主进程在该副本上进行修改操作。
       所以即使对某个数据进行了修改,Redis持久化到RDB中的数据也是未修改的数据,这也是把RDB文件称为"快照"文件的原因,
       子进程所看到的数据在它被创建的一瞬间就固定下来了,父进程修改的某个数据只是该数据的复制品。这里再深入一点,Redis内存中的全量数据由一个个的"数据段页面"组成,
       每个数据段页面的大小为4K,客户端要修改的数据在哪个页面中,就会复制一份这个页面到内存中,这个复制的过程称为"页面分离",在持久化过程中,随着分离出的页面越来越多,
       内存就会持续增长,但是不会超过原内存的2倍,因为在一次持久化的过程中,几乎不会出现所有的页面都会分离的情况,读写请求针对的只是原数据中的小部分,
       大部分Redis数据还是"冷数据"。正因为修改的部分数据会被额外的复制一份,所以会占用额外的内存,当在进行RDB持久化操作的过程中,与此同时如果持续往redis中写入的数据量越多,
       就会导致占用的额外内存消耗越大。
  那么在此期间写入的数据最终去哪了呢? 
  写入的数据还是存在了内存当中,并没有写入当前的持久化文件中,等到下次进行RDB持久化时才会把 ” 写入的数据 ” 落盘到RDB文件中。
  bgsave:fork出的子进程开始根据父进程内存数据生成临时的快照文件,然后替换原文件。
  如何查看redis持久化信息?
  127.0.0.1:6379> info persistence
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1697179568
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:516096
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0


 这里解释一下几个跟 RDB 相关的参数:

rdb_changes_since_last_save:自上次 RDB 后,Redis 数据的改动条数
rdb_bgsave_in_progress:bgsave 是否在进行中,0 否,1 是
rdb_last_save_time:上次 bgsave 的时间戳
rdb_last_bgsave_status:上次 bgsave 的状态
rdb_last_bgsave_time_sec:上次 bgsave 的持续时间
rdb_current_bgsave_time_sec:正在执行的 bgsave 耗时,如果没有正在执行的,则为 -1
rdb_last_cow_size:上一次RDB保存操作期间写时复制分配的大小(以字节为单位)
   
4.flushall,flushdb,shutdown测试结果:
flushall,flushdb只会清理缓存,不会触发持久化,如果设置了save m n 配置或者手动执行save bgsave才会持久化
loading:0
rdb_changes_since_last_save:100010
rdb_bgsave_in_progress:0
rdb_last_save_time:1697509576
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0

5. RDB的优点
RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复。
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。
  RDB的缺点
耗时、耗性能。RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,
AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。
不可控、丢失数据。如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,
你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据。
      
6.触发RDB持久化查看日志文件redis_6379.log
 触发条件:save 60 10000
 java程序:for (int i=0;i<100000;i++)
        {
        jedis.set("name"+i,"tomandjjjyyywhoismyname"+i);
        } 
11:09:12 启动redis
11:15:25 执行java程序
11:15:28.532 触发10000 changes in 60 seconds,然后开始持久化
11:15:28.632 持久化结束
11:16:29.062再次触发触发10000 changes in 60 seconds,然后开始持久化
11:16:29.163 持久化结束
注意:RDB: 0 MB of memory used by copy-on-write,是因为java程序没有写脏页
        
3965945:C 13 Oct 2023 11:09:12.109 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3965945:C 13 Oct 2023 11:09:12.109 # Redis version=6.0.5, bits=64, commit=00000000, modified=0, pid=3965945, just started
3965945:C 13 Oct 2023 11:09:12.109 # Configuration loaded
3965946:M 13 Oct 2023 11:09:12.111 * Running mode=standalone, port=6379.
3965946:M 13 Oct 2023 11:09:12.111 # Server initialized
3965946:M 13 Oct 2023 11:09:12.111 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
3965946:M 13 Oct 2023 11:09:12.111 * Loading RDB produced by version 6.0.5
3965946:M 13 Oct 2023 11:09:12.112 * RDB age 158897 seconds
3965946:M 13 Oct 2023 11:09:12.112 * RDB memory usage when created 0.85 Mb
3965946:M 13 Oct 2023 11:09:12.112 * DB loaded from disk: 0.000 seconds
3965946:M 13 Oct 2023 11:09:12.112 * Ready to accept connections
3965946:M 13 Oct 2023 11:15:28.532 * 10000 changes in 60 seconds. Saving...
3965946:M 13 Oct 2023 11:15:28.532 * Background saving started by pid 3966668
3966668:C 13 Oct 2023 11:15:28.542 * DB saved on disk
3966668:C 13 Oct 2023 11:15:28.543 * RDB: 0 MB of memory used by copy-on-write
3965946:M 13 Oct 2023 11:15:28.632 * Background saving terminated with success
3965946:M 13 Oct 2023 11:16:29.062 * 10000 changes in 60 seconds. Saving...
3965946:M 13 Oct 2023 11:16:29.063 * Background saving started by pid 3966814
3966814:C 13 Oct 2023 11:16:29.143 * DB saved on disk
3966814:C 13 Oct 2023 11:16:29.144 * RDB: 0 MB of memory used by copy-on-write
3965946:M 13 Oct 2023 11:16:29.163 * Background saving terminated with success

AOF:

appendonly yes   
appendfilename "appendonly_6379.aof" 
appendfsync everysec  
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100  
auto-aof-rewrite-min-size 64mb  
aof-load-truncated yes  
rename-command FLUSHDB ""
rename-command FLUSHALL ""

1.appendfsync的三种选项:
appendfsync always 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
appendfsync everysec 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
appendfsync no 从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
 测试:通过java程序循环set 10W个KEY,发现everysec和no性能一样,而always确实慢一些
 always: 16:51:22-16:52:10   48秒
 everysec: 16:45:20-16:45:50 30秒
 no:       16:46:43-16:47:13 30秒

2.AOF重写触发条件:

1).自动触发:
在redis.conf中有如下两个配置项来决定是否触发aof重写操作:
auto-aof-rewrite-percentage 100 指当前aof文件比上次重写的增长比例大小,达到这个大小就进行 aof 重写。
auto-aof-rewrite-min-size 64mb 最开始aof文件必须要达到这个文件时才触发,后面的每次重写就不会根据这个变量了。
在 aof 文件小于64mb的时候不进行重写,当到达64mb的时候,就重写一次。重写后的 aof 文件可能是10mb。上面配置了auto-aof-rewrite-percentage 100,即 aof 文件到了20mb的时候,又可以开始重写。
2).手动触发:
也可以通过执行bgrewriteaof来手动触发。

3.AOF重写触发条件:
AOF文件重写过程与RDB快照bgsave工作过程类似似,都是通过fork子进程,由子进程完成相应的操作。
开始bgrewriteaof,判断当前有没有bgsave命令(RDB持久化)/bgrewriteaof在执行,倘若有,则这些命令执行完成以后在执行。
主进程fork出子进程来进行aof重写操作。rewrite会像replication一样,创建一个临时文件,遍历数据库,将每个key、value对输出到临时文件。输出格式就是Redis的命令
主进程fork完子进程后继续接受客户端请求。此时,客户端的写请求不仅仅写入aof_buf缓冲区,还写入aof_rewrite_buf重写缓冲区。一方面是写入aof_buf缓冲区并根据appendfsync策略同步到磁盘,
保证原有AOF文件完整和正确。另一方面写入aof_rewrite_buf重写缓冲区,保存fork之后的客户端的写请求,防止新AOF文件生成期间丢失这部分数据。
子进程写完新的AOF文件后,向主进程发信号,主进程把aof_rewrite_buf中的数据写入到新的AOF文件。

4.触发AOF持久化查看日志文件redis_6379.log
16:45:34 redis重启
16:59:56.242 触发automatic rewriting of AOF
16:59:57.301 子进程完成了持久化内存数据到临时AOF文件,并请求父进程停止写aof_rewrite_buf
16:59:57.301 父进程停写aof_rewrite_buf,定稿AOF文件
16:59:57.301 子进程将0.15 MB of AOF的缓存写入AOF文件
16:59:57.304 * AOF rewrite: 3 MB of memory used by copy-on-write写时复制技术用到了3M的内存
16:59:57.344 AOF rewrite完成
16:59:57.344 * Residual parent diff successfully flushed to the rewritten AOF (0.01 MB),把新产生的AOF 缓存刷新到新的AOF文件里

334936:M 16 Oct 2023 16:45:34.541 # Server initialized
334936:M 16 Oct 2023 16:45:34.541 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
334936:M 16 Oct 2023 16:45:34.542 * Reading RDB preamble from AOF file...
334936:M 16 Oct 2023 16:45:34.542 * Loading RDB produced by version 6.0.5
334936:M 16 Oct 2023 16:45:34.542 * RDB age 431611 seconds
334936:M 16 Oct 2023 16:45:34.542 * RDB memory usage when created 10.18 Mb
334936:M 16 Oct 2023 16:45:34.543 * RDB has an AOF tail
334936:M 16 Oct 2023 16:45:34.617 * Reading the remaining AOF tail...
334936:M 16 Oct 2023 16:45:34.635 * DB loaded from append only file: 0.093 seconds
334936:M 16 Oct 2023 16:45:34.635 * Ready to accept connections
334936:M 16 Oct 2023 16:59:56.242 * Starting automatic rewriting of AOF on 106% growth
334936:M 16 Oct 2023 16:59:56.243 * Background append only file rewriting started by pid 336890
334936:M 16 Oct 2023 16:59:57.301 * AOF rewrite child asks to stop sending diffs.
336890:C 16 Oct 2023 16:59:57.301 * Parent agreed to stop sending diffs. Finalizing AOF...
336890:C 16 Oct 2023 16:59:57.301 * Concatenating 0.15 MB of AOF diff received from parent.
336890:C 16 Oct 2023 16:59:57.302 * SYNC append only file rewrite performed
336890:C 16 Oct 2023 16:59:57.304 * AOF rewrite: 3 MB of memory used by copy-on-write
334936:M 16 Oct 2023 16:59:57.344 * Background AOF rewrite terminated with success
334936:M 16 Oct 2023 16:59:57.344 * Residual parent diff successfully flushed to the rewritten AOF (0.01 MB)
334936:M 16 Oct 2023 16:59:57.344 * Background AOF rewrite finished successfully

aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:3715072
module_fork_in_progress:0
module_fork_last_cow_size:0
aof_current_size:5460006
aof_base_size:2239885
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

这里解释一下几个跟 AOF 相关的参数:
aof_rewrite_scheduled:rewrite任务计划,当客户端发送bgrewriteaof指令,如果当前rewrite子进程正在执行,那么将客户端请求的bgrewriteaof变为计划任务,待aof子进程结束后执行rewrite
aof_last_bgrewrite_status:上次bgrewriteaof操作的状态
aof_last_write_status:上次aof写入状态
aof_last_cow_size:AOF过程中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等)。和rdb是一样的。rdb_last_cow_size:上一次RDB保存操作期间写时复制分配的大小(以字节为单位)
aof_pending_bio_fsync ;//IO队列中等待执行fsync的数量,属性的值是否为0,如果是的活就表明服务器已经将已知的所有数据都保存到硬盘里了。
aof_delayed_fsync:0  //被延迟的fysnc数量

flushall,flushdb测试结果:

flushall,会清空所有数据库的缓存并将该命令添加进appendonly.aof 
flushdb  会清空当前数据库的缓存并将该命令添加进appendonly.aof 

             


             

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值