首页 > 代码库 > Redis持久化

Redis持久化

Redis 持久化


 

  1. RDB(快照)

    优点 

    • rdb是可进行压缩的二进制文件,表示Redis在某一个时间点的数据快照。非常使用与备份,灾难恢复等场景.比如使用定时任务执行bgsave并备份rdb到server或其他文件系统中,用于恢复数据.
    • rdb加载速度快于AOF方式

    缺点

    • RDB不可以做到实时持久化,容易造成数据丢失,假如频繁使用bgsave强行实时持久化,会非常影响性能,因为创建fork子进程也是有阻塞产生的
    • RDB使用特定的二进制保存,随着redis的不断更新,会有多个格式的RDB版本,会产生兼容问题

 

  2. AOF(追加)

    优点

    • 比RDB要可靠,可以定制不同的fsync策略(appendfsync everysec/no/always),默认是appendfsync everysec,每秒fsync一次,意味着最多丢失1s的数据
    • AOF是一个纯追加的文件,遇到宕机基本也不会影响磁盘上的aof日志文件
    • 档aof太大时,redis会自动进行重写(重写在一个新的文件,与此同时reids会继续往旧的aof文件追加日志信息,当新文件写完时会用新文件覆盖旧文件,之后日志信息开始往新文件追加)

    缺点

    • 相同数据集,aof文件要大于rdb文件
    • AOF速度稍慢与RDB

 

 

RDB 


 

 RDB持久化是把当前进程数据生成的快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发

 

  1.手动触发,使用save和bgsave命令

 

    • save:阻塞当前redis服务器,直到持久化完成为止,对于内存较大的实例会造成长时间的阻塞,线上环境禁止使用,已经被废弃.
root@e7576f2fbb11:/# redis-cli save
OK
root@e7576f2fbb11:/# redis-cli info|grep save        #查看持久化相关信息
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1497083505
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
root@e7576f2fbb11:/# tail -n 4 /var/log/redis/redis-server.log
61:C 10 Jun 08:20:46.257 * DB saved on disk
61:C 10 Jun 08:20:46.258 * RDB: 0 MB of memory used by copy-on-write
29:M 10 Jun 08:20:46.316 * Background saving terminated with success
29:M 10 Jun 08:31:45.676 * DB saved on disk            ###运行redis-cli save对应的redis服务日志
    • bgsave: redis进程执行fork操作创建子进程,RDB持久化由这个子进程负责,完成后自动结束.阻塞仅仅会发生在fork阶段,时间非常非常的短,bgsave是对save的优化.
root@e7576f2fbb11:/# redis-cli bgsave
Background saving started
root@e7576f2fbb11:/# redis-cli info|grep fork
latest_fork_usec:771                  #####最近一次fork子进程阻塞用的时间us
root@e7576f2fbb11:/# redis-cli info|grep save     #####获取持久化相关信息
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1497084106             ####最后一个RDB时间
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
root@e7576f2fbb11:/# tail -n 4 /var/log/redis/redis-server.log    ####使用bgsave持久化redis服务产生的日志
29:M 10 Jun 08:41:46.346 * Background saving started by pid 77
77:C 10 Jun 08:41:46.380 * DB saved on disk
77:C 10 Jun 08:41:46.381 * RDB: 0 MB of memory used by copy-on-write
29:M 10 Jun 08:41:46.385 * Background saving terminated with success

  2.自动触发

    2.1 通过reids.conf配置动触发bgsave

    2.2 当从节点执行全量复制操作时,主节点自动执行bgsave生成bgsave RDB文件并发送给从节点.

    2.3 当使用redis-cli  shutdown关闭redis服务时,如果没有开启AOF持久化功能则也会自动执行bgsave

root@e7576f2fbb11:/# grep ‘save‘ /etc/redis/redis.conf
#   save <seconds> <changes>
#   Will save the DB if both the given number of seconds and the given
#   In the example below the behaviour will be to save:
#   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
#   save ""
save 900 1        ###900s内至少一次改动,自动触发bgsave
save 300 10        ###300s内至少10次改动,自动触发bgsave
save 60 10000      ###60s内至少10000次改动,自动触发bgsave
# save ""        ###如果最后一行是save ""则上面自动触发bgsave规则会失效

  3.bgsave运作流程

  • 执行bgsave命令后,redis主进程判断当前是否存在正在执行的(AOF或者RDB)子进程,如果存在则直接返回bgsave命令.
    • root@e7576f2fbb11:/# redis-cli bgsave;redis-cli bgsave
      Background saving started
      (error) ERR Background save already in progress
  • 父进程fork子进程过程中,父进程会短暂的阻塞,通过redis-cli info|grep fork可以获得最近一个fork的操作耗时,单位为us,微秒.
  • 父进程fork子进程成功后,返回Background saving started,并停止阻塞主进程,可以继续响应其它指令.
  • 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次RDB生成的时间.
  • 子进程发送信号给父进程表示完成,父进程更新统计信息.redis-cli  info可以查看redis状态.

  4.RDB文件相关信息

  

root@e7576f2fbb11:/# egrep -i "filename|dir" /etc/redis/redis.conf
# line as value of a configuration directive, you‘d better put includes
# interfaces using the "bind" configuration directive, followed by one or
#   points by adding a save directive with a single empty string argument
# The filename where to dump the DB
dbfilename dump.rdb          #RDB文件保存的文件名,默认采取压缩
# The working directory.
# The DB will be written inside this directory, with the filename specified
# above using the ‘dbfilename‘ configuration directive.
# The Append Only File will also be created inside this directory.
# Note that you must specify a directory here, not a file name.
dir /var/lib/redis          ###RDB文件保存目录

 在redis正常运行过程中如果默认的RDB存储路径磁盘写满或不能正常写时,可以使用config set dbfilename newfilename和config set dir newdir动态改变默认的存储路径.

root@e7576f2fbb11:/# redis-cli -h 127.0.0.1 -p 6379  config set dir /var/lib/
OK
root@e7576f2fbb11:/# redis-cli -h 127.0.0.1 -p 6379  config set filename newfilename.rdb
(error) ERR Unsupported CONFIG parameter: filename
root@e7576f2fbb11:/# redis-cli -h 127.0.0.1 -p 6379  config set dbfilename newfilename.rdb
OK
root@e7576f2fbb11:/# redis-cli bgsave
Background saving started
root@e7576f2fbb11:/# ls -l /var/lib/newfilename.rdb
-rw-r--r-- 1 root root 34 Jun 10 09:28 /var/lib/newfilename.rdb
root@e7576f2fbb11:/# date
Sat Jun 10 09:29:14 UTC 2017
root@e7576f2fbb11:/#
root@e7576f2fbb11:/# redis-check-dump /var/lib/newfilename.rdb        #####使用redis-check-dump 工具检测RDB文件是否有问题.
==== Processed 3 valid opcodes (in 17 bytes) ===================================
CRC64 checksum is OK

 

AOF

 


 

AOF(append only file)持久化:使用独立的日志的方式记录每次写的命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的.AOF的主要作用是解决了数据持久化的实时性.

如果要开启AOF需要修改redis.conf配置:appendonly  yes(默认不开启:appendonly no)

通过定义appendfilename和dir修改aof文件名和保存路径.

AOF工作流程:

  1. 所有的写入命令追加到缓冲区中(redis是单线程响应方式,如果实时追加到硬盘,会影响性能.同时也是fsync策略起作用的前提)
  2. AOF缓冲区根据对应的fsync策略向硬盘做同步操作
  3. 随着AOF文件越来越大,需要定期AOF文件进行重写,达到"压缩"文件的目的
  4. 重启redis时,可以加载AOF文件进行数据恢复.

 

文件同步

  redis提供多种缓冲区同步文件策略,由参数appendfsync控制,不同值含义如下:

<style>html,body { margin: 0; padding: 0 } body { padding: 20px } h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td { margin: 0; padding: 0; border: 0; font-weight: normal; font-style: normal; font-size: 100%; line-height: 1; font-family: inherit } table { border-collapse: collapse; border-spacing: 0 } ol,ul { list-style: none } q::before,q::after,blockquote::before,blockquote::after { content: "" } html { font-size: 100% } a:focus { outline: thin dotted } a:hover,a:active { outline: 0 } article,aside,details,figcaption,figure,footer,header,hgroup,nav,section { display: block } audio,canvas,video { display: inline-block } audio:not([controls]) { display: none } sub,sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline } sup { top: -0.5em } sub { bottom: -0.25em } img { border: 0 } button,input,select,textarea { font-size: 100%; margin: 0; vertical-align: baseline } button,input { line-height: normal } button::-moz-focus-inner,input::-moz-focus-inner { border: 0; padding: 0 } button,input[type="button"],input[type="reset"],input[type="submit"] { cursor: pointer } input[type="search"] { } input[type="search"]::-webkit-search-decoration { } textarea { overflow: auto; vertical-align: top } html,body { background-color: #ffffff } body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: 18px; color: #404040 } .container { width: 940px; margin-left: auto; margin-right: auto } .container::before,.container::after { display: table; content: "" } .container::after { clear: both } .container-fluid { position: relative; min-width: 940px; padding-left: 20px; padding-right: 20px } .container-fluid::before,.container-fluid::after { display: table; content: "" } .container-fluid::after { clear: both } .container-fluid>.sidebar { float: left; width: 220px } .container-fluid>.content { margin-left: 240px } a { color: #0069d6; text-decoration: none; line-height: inherit; font-weight: inherit } a:hover { color: #00438a; text-decoration: underline } .pull-right { float: right } .pull-left { float: left } .hide { display: none } .show { display: block } .row { margin-left: -20px } .row::before,.row::after { display: table; content: "" } .row::after { clear: both } p { font-size: 13px; font-weight: normal; line-height: 18px; margin-bottom: 9px } p small { font-size: 11px; color: #bfbfbf } h1,h2,h3,h4,h5,h6 { font-weight: bold; color: #404040 } h1 small,h2 small,h3 small,h4 small,h5 small,h6 small { color: #bfbfbf } h1 { margin-bottom: 18px; font-size: 30px; line-height: 36px } h1 small { font-size: 18px } h2 { font-size: 24px; line-height: 36px } h2 small { font-size: 14px } h3,h4,h5,h6 { line-height: 36px } h3 { font-size: 18px } h3 small { font-size: 14px } h4 { font-size: 16px } h4 small { font-size: 12px } h5 { font-size: 14px } h6 { font-size: 13px; color: #bfbfbf; text-transform: uppercase } ul,ol { margin: 0 0 18px 25px } ul ul,ul ol,ol ol,ol ul { margin-bottom: 0 } ul { list-style: disc } ol { list-style: decimal } li { line-height: 18px; color: #808080 } ul.unstyled { list-style: none; margin-left: 0 } dl { margin-bottom: 18px } dl dt,dl dd { line-height: 18px } dl dt { font-weight: bold } dl dd { margin-left: 9px } hr { margin: 20px 0 19px; border: 0; border-bottom: 1px solid #eee } strong { font-style: inherit; font-weight: bold } em { font-style: italic; font-weight: inherit; line-height: inherit } .muted { color: #bfbfbf } blockquote { margin-bottom: 18px; border-left: 5px solid #eee; padding-left: 15px } blockquote p { font-size: 14px; font-weight: 300; line-height: 18px; margin-bottom: 0 } blockquote small { display: block; font-size: 12px; font-weight: 300; line-height: 18px; color: #bfbfbf } blockquote small::before { content: "—?" } address { display: block; line-height: 18px; margin-bottom: 18px } code,pre { padding: 0 3px 2px; font-family: Monaco, Andale Mono, Courier New, monospace; font-size: 12px } code { padding: 1px 3px } pre { background-color: #f5f5f5; display: block; padding: 8.5px; margin: 0 0 18px; line-height: 18px; font-size: 12px; border: 1px solid rgba(0, 0, 0, 0.15); white-space: pre-wrap } form { margin-bottom: 18px } fieldset { margin-bottom: 18px; padding-top: 18px } fieldset legend { display: block; padding-left: 150px; font-size: 19.5px; line-height: 1; color: #404040 } form .clearfix { margin-bottom: 18px } form .clearfix::before,form .clearfix::after { display: table; content: "" } form .clearfix::after { clear: both } label,input,select,textarea { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: normal } label { padding-top: 6px; font-size: 13px; line-height: 18px; float: left; width: 130px; text-align: right; color: #404040 } form .input { margin-left: 150px } input[type="checkbox"],input[type="radio"] { cursor: pointer } input,textarea,select,.uneditable-input { display: inline-block; width: 210px; height: 18px; padding: 4px; font-size: 13px; line-height: 18px; color: #808080; border: 1px solid #ccc } input[type="checkbox"],input[type="radio"] { width: auto; height: auto; padding: 0; margin: 3px 0; line-height: normal; border: none } input[type="file"] { background-color: #ffffff; padding: initial; border: initial; line-height: initial } input[type="button"],input[type="reset"],input[type="submit"] { width: auto; height: auto } select,input[type="file"] { height: 27px; line-height: 27px } select[multiple] { height: inherit } textarea { height: auto } .uneditable-input { background-color: #ffffff; display: block; border-color: #eee; cursor: not-allowed } :-moz-placeholder { color: #bfbfbf } ::-webkit-input-placeholder { color: #bfbfbf } input,textarea { } input:focus,textarea:focus { outline: 0; border-color: rgba(82, 168, 236, 0.8) } input[type="file"]:focus,input[type="checkbox"]:focus,select:focus { outline: 1px dotted #666 } form div.clearfix.error { background: #fae5e3; padding: 10px 0; margin: -10px 0 10px } form div.clearfix.error>label,form div.clearfix.error span.help-inline,form div.clearfix.error span.help-block { color: #9d261d } form div.clearfix.error input,form div.clearfix.error textarea { border-color: #c87872 } form div.clearfix.error input:focus,form div.clearfix.error textarea:focus { border-color: #b9554d } form div.clearfix.error .input-prepend span.add-on,form div.clearfix.error .input-append span.add-on { background: #f4c8c5; border-color: #c87872; color: #b9554d } table { width: 100%; margin-bottom: 18px; padding: 0; border-collapse: separate; font-size: 13px; border: 1px solid #ddd } table th,table td { padding: 10px 10px 9px; line-height: 18px; text-align: left } table th { padding-top: 9px; font-weight: bold; vertical-align: middle; border-bottom: 1px solid #ddd } table td { vertical-align: top } table th+th,table td+td { border-left: 1px solid #ddd } table tr+tr td { border-top: 1px solid #ddd } table tbody tr:first-child td:first-child { } table tbody tr:first-child td:last-child { } table tbody tr:last-child td:first-child { } table tbody tr:last-child td:last-child { } .zebra-striped tbody tr:nth-child(2n+1) td { background-color: #f9f9f9 } .zebra-striped tbody tr:hover td { background-color: #f5f5f5 } .zebra-striped .header { cursor: pointer } .zebra-striped .header::after { content: ""; float: right; margin-top: 7px; border-width: 0 4px 4px; border-style: solid; border-color: #000 transparent; visibility: hidden } .zebra-striped .header:hover::after { visibility: visible } footer { margin-top: 17px; padding-top: 17px; border-top: 1px solid #eee } .page-header { margin-bottom: 17px; border-bottom: 1px solid #ddd } .page-header h1 { margin-bottom: 8px } .close { float: right; color: #000000; font-size: 20px; font-weight: bold; line-height: 13.5px; opacity: 0.2 } .close:hover { color: #000000; text-decoration: none; opacity: 0.4 } pre { padding: 0; margin: 10px 0px 10px; overflow: auto } pre code { margin: 5px; padding: 0px; display: block; line-height: 18px } .center { text-align: center } .left { text-align: left } .right { text-align: right }</style> <style>body { font-family: "Geneva", Arial, sans-serif; font-size: 13px; margin: 10px } a,a:visited { color: #09c } a:hover { color: #336699; text-decoration: none } h1 { margin: 0px 0px 10px; font-weight: bold } h2 { border-bottom: 2px dotted #ccc; margin: 5px 0px 15px } h6 { color: #09c } blockquote { font-family: "Georgia", Courier New, courier, sans-serif; background: #efefef; padding: 5px 10px; border: solid 1px #ddd; margin: 15px; color: #333 } ul,ol { margin-bottom: 15px } li { padding: 3px } code { background-color: #f1f1f1; color: #336699 } pre { background-color: #f1f1f1 } pre>code { margin: 0px; padding: 5px; border: 0px; background-color: #f1f1f1 }</style>
可配置值说明
always 命令写入aof_buf后调用fsync同步操作同步到aof文件,fsync同步完成后线程返回
everysec 命令写入aof_buf后调用系统write操作,write完成后线程返回,fsync操作由专门线程每秒调用一次 (建议配置)
no 命令写入aof_buf后调用系统write操作,不对aof文件做fsync同步,同步硬盘由操作系统负责,通常最长同步周期30秒

系统调用write和fsync说明

  • write 操作会触发延迟写机制.Linux在内核提供页缓冲区用来提高IO性能.write操作在写入系统缓冲区后直接返回.同步硬盘操作依赖于系统调度的机制,比如当缓冲区页空间写满或达到特定时间周期时触发.同步文件之前,如果此时系统宕机,缓冲区数据会丢失.
  • fsync这对单个文件操作,做强制磁盘同步,fsync将阻塞知道写入硬盘完成后返回,保证数据持久化.

 

重写机制

  随着不断的追加写入,aof文件越来越大,此时重写机制会压缩文件大小.重写指的是:把redis进程内的数据转化为写命令同步到新AOF文件的过程.

  重写使aof文件变小原因:

    1. 过期的数据不会写入
    2. 多余的命令不会写入
    3. 多条命令的合并(lpush list a lpush list b lpush c====>lpush list a b c)  

  重写的触发

    • 手动调用  bgrewriteaof命令
    • 自动触发  根据配置文件中auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
      • auto-aof-rewrite-min-size: 表示重写aof时,文件的最小体积,default64MB(重写优先条件,必须满足)
      • auto-aof-rewrite-min-percentage: 表示当前aof文件大小和上次重写后aof文件大小的比值.

         

         

  重写运作流程

    1. 执行bgrewriteaof请求如果当前正字执行aof,直接返回,如果在执行bgsave则推迟到bgsave完成后再执行
    2. redis调用fork产生一个子进程(开销同RDB)
    3. 主进程持续把新的变动写到aof_buf并仍然沿用appendfsync策略同步到磁盘,即旧的aof文件,由于fork使用写时复制技术,子进程只能共享fork操作时的内存数据,所以新的变动同时也要在aof_rewrite_buf存一份,防止新的aof文件生成期间丢失这部分数据
    4. 根据规则重写aof文件(新)
    5. 新aof文件写入完成后,子进程通知父进程,父进程更新信息.
    6. 父进程把aof_rewrite_buf写入新的aof文件 
    7. 新aof文件替换老的aof文件

技术分享

 

 

 

重启加载


 

1. AOF持久化开启且存在AOF文件时,优先加载AOF文件

 

2. AOF关闭或AOF文件不存在时,加载RDB文件(前提是RDB存在,不存在也会启动)

 

3.加载AOF/RDB文件成功后,rdis启动成功

 

4. AOF/RDB存在错误时,启动失败

 

Redis持久化