十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redis持久化详解:RDB、AOF与混合持久化,数据安全最佳实践

Redis持久化详解:RDB、AOF与混合持久化,数据安全最佳实践 Redis用了这么多年时不时还会被人问起Redis重启了数据还在吗Redis挂了会不会丢数据。你要是直接告诉对方Redis是内存数据库、数据放内存里那只能说了解了一半。Redis真正让我觉得靠谱的地方是它的持久化机制——它能在进程退出、系统断电甚至服务器宕机之后把数据恢复到内存里。这篇我想把RDB和AOF这两套方案的原理、配置、优缺点和实际选用逻辑一次性讲透顺便把我在运维过程中踩过的坑也摆出来供你参考。1. Redis持久化到底解决什么问题1.1 没有持久化时Redis就像一个失忆的天才Redis能把数据存到内存读写速度可以到每秒十万级别这是它的核心优势。但内存本身是易失的——断电就清零进程崩溃也一样。你如果不做额外处理重启之后Redis就是一片空白缓存还能接受回源重建可如果里面存的是会话凭证、排行榜、未落库的订单状态这类数据那一次意外宕机就是实打实的线上事故。我遇到过最典型的一次是某业务把用户登录token放在Redis里有效期设为7天。后来一台机器突然掉电Redis没有配置任何持久化重启后所有token全部消失几百个用户同一时间被踢下线那场面相当酸爽。从那时候起我就形成了一个习惯线上Redis持久化默认必开除非这个实例明确充当纯缓存且允许全部丢失。1.2 持久化在Redis整体架构里的角色持久化解决的是内存数据如何应对进程退出的问题。它和主从复制、哨兵、集群这些高可用机制互相配合各司其职。持久化解决的是单机数据恢复问题进程退出、机器重启后能从磁盘恢复。主从复制解决的是多机数据冗余问题主节点挂了从节点能顶上。哨兵和集群负责故障转移和横向扩展。如果完全没有持久化就算你有主从主节点一挂从节点顶上可如果主节点重启了里面是空的从节点一同步反而会把从节点里的数据也清空这就是俗称的主从都空了。所以持久化是整个Redis高可用体系的最后一道防线这一点一定要想清楚。1.3 两套方案照相机和记账本Redis官方提供了两种持久化方案理解起来很生活化。RDBRedis DataBase像一部照相机定时给内存里的全量数据拍一张快照存成一个二进制文件默认叫dump.rdb。恢复的时候直接把这个快照读进内存即可速度很快。缺点是两次拍照之间写入的数据如果中途宕机就会丢。AOFAppend Only File像一本记账本每次写操作都会追加记录到文件里。恢复的时候把记账本从头到尾重放一遍内存重新算出数据。它的优点是数据更完整最多丢1秒甚至不丢数据缺点是文件会越来越大恢复速度也相对较慢。光看这段你可能还是不知道该怎么选。实际上持久化是可以用组合拳的Redis从4.0开始主推的混合持久化就是把照相机和记账本合并既保证恢复速度又保证数据安全。这个后面详细说。2. RDB快照一台定期拍照的相机2.1 RDB的工作机制fork和Copy-On-WriteRDB最核心的动作是生成快照。Redis不会直接把当前内存数据一把梭写到磁盘而是利用操作系统的fork机制派生出一个子进程来做实际的写入。具体流程是这样的Redis主进程调用fork生成一个与自己几乎完全一样的子进程。子进程负责把内存里的数据遍历一遍写入临时RDB文件。写完临时文件后原子地替换掉旧RDB文件快照完成。主进程完全不阻塞照常处理请求。这里的关键是Linux的写时复制Copy-On-Write。fork出来的子进程共享父进程的内存页只有当父进程收到写请求、要修改某个内存页时才会把该页复制一份给子进程使用。也就是说子进程看到的快照是fork那一刻的数据视图而不是新写入的数据。这个机制会产生一个副作用如果fork期间父进程有大量写操作父进程就需要复制大量内存页内存占用会临时上升也可能引发短暂的停顿。这个坑我在后面第6章详细讲。2.2 触发方式手工命令和配置自动触发手工触发Redis提供了两个命令SAVE同步生成RDB文件。执行期间Redis会阻塞不处理任何其他命令。生产环境基本不用除非你明确知道这个实例可以停摆。BGSAVE后台异步生成RDB文件。Redis会fork子进程来完成主进程照常服务。生产环境推荐用这个。自动触发自动触发靠的是配置文件里的save参数规则是N秒内发生了M次写操作就执行bgsave。默认配置长这样save 900 1 save 300 10 save 60 10000意思是900秒内至少1次写操作触发一次bgsave。300秒内至少10次写操作触发一次bgsave。60秒内至少10000次写操作触发一次bgsave。三个条件满足任意一个就会触发。注意写进日志的话你可能看到的是DB saved on disk说明bgsave成功执行了。另外还有一些情况会自动触发RDB比如主从复制时从节点会收到主节点传来的RDB文件比如执行SHUTDOWN时如果开启了save-on-shutdown相关配置再比如通过FLUSHALL清库防止清库后没有备份没法恢复建议清库前手动bgsave一次。2.3 关键配置参数解析配置项集中在redis.conf这里挑几个决定RDB行为的关键参数。配置项默认值说明save900 1 / 300 10 / 60 10000自动快照触发条件不需要可全部注释掉stop-writes-on-bgsave-erroryesbgsave失败时是否拒绝新的写入请求rdbcompressionyes是否对RDB文件进行LZF字符串压缩rdbchecksumyes读取RDB文件时是否做CRC64校验dbfilenamedump.rdbRDB文件名dir./RDB文件和AOF文件的存放目录这里特别说一下stop-writes-on-bgsave-error。默认yes的意思是磁盘满了、权限不够导致bgsave失败时Redis会拒绝所有写入操作。为什么这么设计因为如果写失败还继续接收写入那磁盘上的旧RDB文件就和内存数据差距越来越大等于持久化形同虚设。所以宁可拒绝写入也要暴露问题避免以为自己有备份、实际没有的假象。2.4 什么场景适合用RDBRDB的特性决定了它适合这些场景数据备份和灾难恢复RDB是一个紧凑的二进制文件把它拷贝到对象存储或者远程机器上非常方便。归档、定时备份、接入离线分析都是它的强项。对数据完整性要求不那么极端的场景比如缓存冷数据、Feed流信息、可以重建的热点数据丢失一段时间内的增量是可以接受的。实例重启后的快速恢复RDB是二进制数据恢复时直接加载启动速度远快于AOF重放。不过纯RDB也有个明显的短板两次快照之间的数据会丢。默认配置下极端情况可能丢失近60秒的数据如果你的业务能接受这个级别的损失RDB就够用接受不了就得AOF或者混合持久化上场了。3. AOF日志一份会重放的流水账3.1 AOF的原理把每次写操作记下来AOF的思路和RDB完全不同。它不是记录某一时刻的内存全量而是把每一个会导致数据变更的命令追加到文件末尾。这样一来只要AOF文件里记录的命令是完整的重放一遍就能还原出完整的数据库状态。举个例子你执行了SET name redis之后又执行了INCR counterAOF文件里就多了两条记录。Redis重启时会逐条读取这些命令并重新执行最终得到nameredis、counter1这组数据。因为AOF是文本协议格式所以它某种程度上可以当审计日志用——你能直观看到哪些命令被写入过虽然不建议在生产环境用文本方式去直接编辑AOF因为格式太细碎。3.2 appendfsync策略三种刷盘模式AOF是追加写入但操作系统不会每条命令都立刻写进磁盘数据会先落到OS的Page Cache再由系统决定何时落盘。为了控制最多丢多少数据Redis提供了appendfsync配置选项取值有三个策略行为数据安全性性能always每执行一个写命令就同步刷盘一次最高最多丢一条命令最慢吞吐大幅下降everysec每秒刷一次盘折中最多丢1秒数据较好公认性价比最优no完全交给操作系统决定何时刷盘取决于系统刷盘时机可能丢较多数据最快线上我几乎只用everysec这是官方默认值也是绝大多数生产实例的正确选择。always适合那种绝对不允许丢任何命令的场景比如金融支付流水但代价是写性能会暴跌除非你有充足理由否则别轻易开。no的话数据安全性几乎等于没有不如直说不用AOF省得自欺欺人。3.3 AOF重写机制解决日志无限膨胀AOF的天然问题是文件会越来越大。一个经常启动停服的场景下AOF可能从几十MB膨胀到几百MB甚至几个GB。日志大还只是次要问题真正麻烦的是恢复时间变长——每次重启都要重放海量命令。**AOF重写rewrite**就是为了解决这个问题。它的逻辑不是从旧的AOF文件里修剪而是基于当前内存里的数据状态重新生成一份最小的AOF内容。比如你对某个key做了1000次INCRAOF文件里就有1000条INCR记录重写之后只保留一条SET counter 1000。重写也是采用fork子进程的方式不会阻塞主进程。重写期间如果有新的写命令进来Redis会把这些命令同时写到旧AOF和重写缓冲区里等子进程完成后再把缓冲区的内容追加到新AOF文件末尾最后原子替换旧文件。这个设计能保证重写不丢任何数据。自动触发条件由两个参数决定auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb含义是当AOF文件比上一次重写后的大小增长了100%时且文件超过64MB时触发重写。如果你的业务写入量不大可能很长时间都不会触发一次重写属正常现象。3.4 AOF在实际场景中的取舍AOF的优势是数据安全性高缺点是文件膨胀和恢复速度慢。但这两个缺点在Redis 4.0之后基本被混合持久化补齐了所以纯AOF方案现在已经越来越少见。不过AOF有一个RDB不具备的好处你可以通过BGREWRITEAOF命令手动触发重写从而让AOF文件保持在一个可控的体积。这在备份场景下很好用——先手动重写再备份文件备份体积小、恢复也快。4. RDB与AOF的对比及混合持久化4.1 一张表看懂两者差异对比维度RDBAOF数据完整性两次快照之间可能丢数据everysec最多丢1秒always不丢恢复速度快直接加载二进制快照慢需要逐条重放命令文件体积紧凑相对较小较大需要重写控制性能影响fork期间COW会有开销everysec影响较小always影响大可读性二进制不可读文本协议可读灾难恢复拷走dump.rdb即可需要完整AOF及对应环境典型场景冷备、快速恢复、数据分析数据安全性要求高的业务4.2 混合持久化两全其美的方案Redis 4.0放出了混合持久化aof-use-rdb-preamble默认是开启的。这个方案把RDB和AOF缝合在一起写入阶段是AOF但重写后的AOF文件开头先写入一份RDB快照后面再追加增量命令。打个比方混合持久化生成的文件像一本附了记忆卡快照的账本。你恢复时先加载快照部分速度很快然后只需要重放假发快照之后的那一小段账目数据安全也能兼顾。这是我现在最推荐的方案配置起来就是在持久化配置里同时开启RDB和AOF然后把aof-use-rdb-preamble保持为yes。这样即使AOF文件膨胀触发重写重写产生的也是一个RDB头部AOF尾部的合成体恢复速度接近RDB数据安全接近AOF。4.3 不同业务场景的选型建议这里我给一份可以直接抄的配置建议缓存场景数据丢了也无妨不开启持久化或者只开RDB做兜底冷备。一般业务数据允许丢失几秒RDB AOFeverysec开启混合持久化。这是最通用的配置。金融、订单等强一致数据AOFalways关闭RDB自动触发避免fork干扰同时依赖主从复制高可用。只做备份归档恢复速度优先只开RDB做定时bgsave备份dump.rdb到远端。另外无论怎么选请记住一个原则持久化配置不替代备份策略。RDB文件和AOF文件本身也可能损坏或者因为误操作被覆盖定期把持久化文件复制到独立存储云对象存储、另一台机器才是真正的保险。5. 主从架构和容器部署中的持久化细节5.1 主从复制和持久化的联动关系主从复制本身依赖RDB文件。主节点和从节点建立同步时主节点会执行bgsave生成快照再把快照传给从节点从节点加载快照后继续接收增量命令。这里有一个容易忽略的风险如果主节点关闭了持久化而在同步过程中主节点发生崩溃并自动重启主节点重启后内存是空的从节点却还有旧数据。此时从节点如果检测到主节点数据不一致会尝试重新全量同步结果反而是把从节点清空来匹配空主库。这就是为什么生产环境里哪怕你的主节点主观认为有从节点兜底也强烈建议开启持久化至少开AOF或RDB之一。5.2 Docker部署Redis时持久化文件怎么挂载用Docker跑Redis的非常多这里有个高频坑默认情况下Docker容器销毁后容器内文件系统里的数据就没了。要持久化必须把Redis的数据目录挂载到宿主机而且挂载点必须是dir配置指定的目录。最常见的挂载命令是这样docker run -d \ --name redis \ -p 6379:6379 \ -v /data/redis:/data \ -v /etc/redis/redis.conf:/etc/redis/redis.conf \ redis:7.0 redis-server /etc/redis/redis.conf这里有个特别容易踩的坑Redis官方镜像有个VOLUME声明默认挂在/data目录。如果你用docker-compose但忘了加volumes容器重建后AOF和RDB文件会丢失。所以用容器跑Redis一定要把/data目录或你自定义的dir目录显式挂载出来并且确认配置文件里的dir和挂载点一致。还有一点Docker重启策略加个--restartalways是个好习惯Redis实例崩了或机器重启后能自动拉起持久化文件也在宿主机两者配合才能真正做到服务恢复。5.3 分布式锁等场景和持久化的关系热词里出现了“redis分布式锁”这里多说一句。很多人用Redis实现分布式锁核心是SET key value NX EX seconds。锁的可靠性依赖Redis实例的数据不丢吗严格来说关系不大——锁本身是短命key过期时间一到就没了持久化不持久化影响反而不大。但有一种场景要注意如果你用Redis主从架构实现分布式锁主节点写入了锁但还没同步到从节点主节点就挂了从节点顶上后锁信息丢失另一个客户端就可以拿到同一个锁这就是经典的RedLock问题。要彻底解决需要读多写少、同步复制或者用RedLock算法。这和持久化没有直接关系但在设计高可靠分布式锁时不要天真地以为Redis持久化就能兜住锁的可靠性。锁的可靠性和持久化的可靠性是两套逻辑要分别对待。5.4 备份和恢复的完整操作这里给出一套我常用的备份恢复流程。备份阶段执行BGSAVE生成最新RDB文件。执行BGREWRITEAOF压缩AOF文件如果开了AOF。把dir目录下对应的文件复制到独立存储。恢复阶段停止Redis进程。将备份文件放回dir目录文件名要和配置一致。如果同时有RDB和AOF文件Redis启动时会优先加载AOF因为AOF数据更全。如果想用RDB强制恢复需要临时把AOF关掉或者把AOF文件移走。启动Redis用INFO persistence确认加载状态和rdb_changes_since_last_save等指标。有几个细节容易忽略。第一恢复时优先用AOF文件第二如果RDB和AOF都存在但损坏了一个启动可能会失败所以备份时尽量两个都拷第三文件复制要用cp或scp不要直接移动原文件否则Redis还在运行期间你动了它的数据文件可能出现写冲突。6. 持久化相关的踩坑记录和排查套路6.1 fork阻塞问题内存大、写频繁时的隐形炸弹RDB快照和AOF重写都依赖fork子进程。fork本身很快但在Redis使用大量内存时fork需要复制页表这个过程会阻塞主进程。如果实例内存到了几十GB甚至上百GBfork阻塞可能达到几百毫秒甚至秒级这时候所有请求都会被卡住。我遇到过的情况一台Redis实例内存约20GB业务高峰时触发了bgsave然后监控里发现P99延迟从3ms飙到了800ms。排查下来就是fork耗时太长导致主进程阻塞。这类问题有几种应对思路用INFO stats里的latest_fork_usec查看最近一次fork耗时如果超过几百毫秒就要警惕。避免在业务高峰自动触发bgsave可以把save参数调大或者在低峰期手动执行bgsave。如果实例内存过大考虑拆分实例减小单实例内存。在操作系统层面可以关注vm.overcommit_memory配置建议设为1避免fork时因内存分配策略问题失败。6.2 AOF文件截断或损坏的处理AOF是追加写入如果系统异常断电AOF文件可能在尾部留下不完整的半条命令。默认情况下Redis启动时如果检测到AOF文件被截断会按aof-load-truncated配置决定怎么处理默认是yes也就是忽略尾部错误继续启动并在日志里给出警告。如果AOF文件损坏更严重启动直接失败可以用官方自带的工具修复redis-check-aof --fix appendonly.aof它会扫描AOF文件找到第一条损坏的命令并从那里截断或者尽量恢复能读的部分。修复后的AOF文件可能丢失损坏点之后的所有命令但至少能让实例启动起来。如果AOF文件修不了RDB也损坏了应急方案是直接用一个空实例启动然后靠主从复制从从节点全量同步数据前提是有从节点或者从最近的备份恢复。6.3 配置不当导致的数据丢失案例这里复盘一个真实案例。某团队部署Redis时只开了AOF没开RDB而且appendfsync配置成了no理由是性能优先。结果某次操作系统的Page Cache还没落盘机器直接被强制重启Redis恢复后丢失了近几十秒的数据。业务方炸了因为这条数据刚好是收单回调里的订单状态变更。再看另一个案例有人只开RDB默认save 900 1业务量又不大可能10分钟才一条写操作。这种情况下如果宕机丢失的就是近15分钟的数据。对用户来说系统顯示支付成功但后台没记录这就是事故。所以评估持久化级别不能只看理论要结合自己的写入频率来反向推演最多可能丢多久的数据。这两个案例说明同一个问题持久化配置不能拍脑袋要根据真实写入量评估风险窗口。写一下你的核心写命令量级套进save和appendfsync策略里计算出最大丢失窗口再决定配置。6.4 面试里经常追问的持久化问题热词里有“redis面试题”顺便整理几个常见问题方便你自己复盘。RDB和AOF的区别是什么回答重点数据完整性、恢复速度、文件大小、性能影响的对比。bgsave的原理是什么回答重点fork、子进程、Copy-On-Write。AOF重写会不会阻塞主进程回答重点fork不阻塞重写期间新写入命令进缓冲区重写完成后合并。Redis持久化文件损坏了怎么办回答重点redis-check-aof / redis-check-rdb修复和截断逻辑。为什么主从复制下主节点还是建议开启持久化回答重点主节点重启后为空可能导致数据同步直接把从节点清空。这些问题在面试里本质考的不是背答案而是看你有没有真正理解数据安全边界。一旦你能从丢数据窗口的角度回答基本就稳了。7. 个人经验从配置到巡检的一套完整打法这些年用Redis做持久化我总结了一套自己的固定打法写在这里供参考。第一线上配置我几乎都是RDB AOFeverysec 混合持久化全开。RDB文件用来做定期冷备和快速恢复AOF保证可接受的数据丢失窗口混合持久化则让AOF重写之后依然保有RDB的快速恢复能力。第二巡检时重点看几个指标rdb_last_bgsave_status是否ok、aof_last_bgrewrite_status是否ok、aof_current_size增长速率、latest_fork_usec耗时。任何一项异常都值得调查。持久化文件本身也要定期做恢复演练光备份不演练真出事的时候备份能不能用都是未知数。第三对大内存实例我倾向于把自动保存频率调低改为在低峰期手动用BGSAVE和BGREWRITEAOF。这样可以避免业务高峰时fork造成延迟抖动。第四容器化部署时持久化文件挂载是第一步验收项。创建完容器第一时间检查CONFIG GET dir确认数据目录确实挂载到了宿主机持久化磁盘而不是容器可写层。这套打法谈不上前沿但很稳。Redis的持久化不是那种天天要折腾的功能但一旦出了问题动摇的就是数据安全。提前把方案选好、把健康检查链路搭起来比事后抢救划算太多。
返回列表