宕机了,Redis数据丢了怎么办?

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 前言什么是AOF?三种写回策略日志文件太大怎么办?AOF重写会阻塞主线程吗?AOF的缺点总结什么是RDB?给哪些数据做快照?快照时能够修改数据吗?多久做一次快照?增量快照AOF和RDB混合使用总结总结


前言

Redis作为内存型的数据库,虽然很快,依然有着很大的隐患,一旦「服务器宕机」重启,内存中数据还会存在吗?

很容易想到的一个方案是从后台数据恢复这些数据,如果数据量很小,这倒是一个可行的方案。但是如果数据量过大,频繁的从后台数据库访问数据,压力很大;另外一方面恢复数据的时间极慢。

对于Redis来说,实现数据的持久化和快速恢复是至关重要。

今天这篇文章就来介绍一下Redis持久化的两种机制AOF日志、RDB快照。

什么是 AOF 日志?

AOF(Append Only File)日志称之为「写后日志」,即是命令先执行完成,把数据写入内存,然后才会记录日志。

AOF日志(文本形式)会将收到每一条的命令且执行成功的命令以一定的格式写入到文本中(追加的方式)。

「写后日志有什么好处呢?」 如下:

  1. 对于写前日志无论命令是否执行成功都会被记录,但是Redis的写后日志则只有命令执行成功才会被写入日志,避免了日志中存在错误命令;
  2. 同时由于是命令执行成功之后才会写入日志,因此不会阻塞当前命令的执行。

但是AOF日志也有「潜在的风险」,分析如下:

  1. 由于是写后日志,如果在命令执行成功之后,在日志未写入磁盘之前服务器突然宕机,那重启恢复数据的时候,这部分的数据肯定在日志文件中不存在了,那么将会丢失。(无法通过后台数据库恢复的情况下)
  2. 虽然不会阻塞当前命令的执行,由于记录日志也是在主线程中(Redis是单线程),如果日志写入磁盘的时候突然阻塞了,肯定会影响下一个命令的执行。

为了解决上面的风险,AOF日志提供了三种回写策略。

三种写回策略

AOF机制提供了三种回写策略,这些都在appendfsync配置,如下:

  1. Always(同步写回):命令执行完成,立马同步的将日志写入磁盘
  2. Everysec(每秒写回):命令执行完成后,先将日志写入 AOF 文件的内存缓冲区,每隔一秒把缓冲区中内容写入磁盘。
  3. No(操作系统控制的写回):每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

其实这三中写回策略都无法解决主线程的阻塞和数据丢失的问题,分析如下:

  1. 同步写回:基本不丢失数据,但是每步操作都会有一个慢速的落盘操作,不可避免的影响主线程性能。
  2. 每秒写回:采用一秒写一次到 AOF 日志文件中,但是一旦宕机还是会丢失一秒的数据。
  3. 操作系统控制的写回:在写完缓冲区之后则会写入磁盘,但是数据始终在缓冲区的时间内一旦宕机,数据还是会丢失。

以上三种策略优缺点总结如下表:

策略 优点 缺点
Always 可靠性高,数据基本不丢失 每个写命令都要落盘,性能影响较大
Everysec 性能适中 宕机时丢失一秒数据
No 性能好 宕机时丢失数据较多

日志文件太大怎么办?

随着数据量的增大,AOF日志文件难免会很大,这样将会导致写入和恢复数据都将变得非常慢。此时AOF提供了一种「重写机制」解决这一问题。

重写机制理解起来很简单,即是Redis会创建一个新的AOF日志文件,将每个键值对最终的值用一条命令写入日志文件中。

比如读取了键值对key1:value1,重写机制会在新的AOF日志文件中记录如下一条命令:

set key1 value1

其实即是记录多次修改的最终的值记录在新的AOF日志文件中,这样当恢复数据时可直接执行该命令。

「为什么重写机制能够缩小文件呢?」 当一个键值被多次修改后,AOF日志文件中将会记录多次修改键值的命令,重写机制是根据这个键值最新状态为它生成「写入」命令,这样旧文件中的「多条」命令在重写后的新日志中变成了「一条」命令。

作者画了一张重写流程图,仅供参考,如下:

重写机制流程

AOF重写会阻塞主线程吗?

AOF重写虽然能够缩减日志文件的大小,达到减少日志记录和数据恢复的时间,但是在数据量非常的大情况下把整个数据库重写后的日志写入磁盘是一个非常耗时的过程,难道不会阻塞主线程吗?

「答案是:不会阻塞主线程」;因为AOF重写过程是由后台子进程bgrewriteaof来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

其实重写的过程分为两个阶段:「一个拷贝,两处日志」

「一个拷贝」:指每次执行重写时,主线程都fork一个子线程bgrewriteaof,主线程会把内存数据拷贝一份到子线程,此时子线程中包含了数据库的最新数据。然后子线程就能在不影响主线程的情况下进行AOF重写了。

「两处日志」是什么?如下:

  1. 第一处日志:子线程重写并未阻塞主线程,此时主线程仍然会处理请求,此时的AOF日志仍然正在记录着,这样即使宕机了,数据也是齐全的。第一处日志即是值主线程正在使用的日志。
  2. 第二处日志:指新的AOF重写日志;重写过程中的操作也会被写到重写日志缓冲区,这样重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。此时,我们就可以用新的 AOF 文件替代旧文件了。

「总结」Redis在进行AOF重写时,会fork一个子线程(不会阻塞主线程)并进行内存拷贝用于重写,然后使用两个日志保证重写过程中,新写入的数据不会丢失。

AOF的缺点

虽说进行了日志重写后,AOF日志文件会缩减很多,但是在数据恢复过程中仍然是一条命令一条命令(由于单线程,只能顺序执行)的执行恢复数据,这个恢复的过程非常缓慢。

总结

AOF这种通过逐一记录操作命令的日志方式,提供了三种写回策略保证数据的可靠性,分别是AlwaysEverysecNo,这三种策略在可靠性上是从高到低,而在性能上则是从低到高。

为了避免日志文件过大,Redis提供了重写的机制,每次重写都fork一个子线程,拷贝内存数据进行重写,将多条命令缩减成一条生成键值对的命令,最终重写的日志作为新的日志。

什么是RDB?

RDB(Redis DataBase)是另外一种持久化方式:内存快照。

RDB记录的是「某一个时刻」的内存数据,并不是操作命令。

这种方式类似于拍照,只保留某一时刻的形象。内存快照是将某一时刻的状态以文件的形式写入磁盘。这样即使宕机了,数据也不会丢失,这个快照文件就称为RDB文件。

由于记录的是某个时刻的内存数据,数据恢复非常快的,不需要像AOF日志逐一执行记录的命令。

给哪些数据做快照?

为了保证数据的可靠性,Redis执行的「全量快照」,也就是把内存中的所有数据都写到磁盘中。

随着数据量的增大,一次性把全部数据都写到磁盘中势必会造成线程阻塞,这就关系到Redis的性能了。

针对线程阻塞的问题Redis提供了两个命令,如下:

  1. save:在主线程中执行,会导致主线程阻塞。
  2. bgsavefork一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这是Redis的默认配置。

这样就可以使用bgsave命令执行全量快照,既可以保证数据的可靠性也避免了主线程的阻塞。

快照时能够修改数据吗?

子线程执行全量快照的同时,主线程仍然在接受着请求,读数据肯定没有问题,但是如果个修改了数据,如何能够保证快照的完整性呢?

「举个栗子」:我在T时刻进行全量快照,假设数据量有8G,写入磁盘的过程至少需要20S,在这20S的时间内,一旦内存中的数据发生了修改,则快照的完整性就破坏了。

但是如果在快照时不能修改数据,则对Redis的性能有巨大的影响,对于这个问题,Redis是如何解决的呢?

Redis借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

其实很简单,bgsave命令会fork一个子线程,这个子线程共享所有内存的数据,子线程会读取主线程内存中的数据,将他们写入RDB文件。

写时复制保证数据可修改

如上图,对于键值对A的读取并不会影响子线程,但是如果主线程一旦修改内存中一块数据(例如键值对D),这块数据将会被复制一个副本,然后bgsave子线程会将其写入RDB文件。

多久做一次快照?

快照只是记录某一时刻的数据,一旦时间隔离很久,则服务器一旦宕机,则会丢失那段时间的数据。

比如在T1时间做了一次快照,在T1+t时又做了一次快照,如果在t这个时间段内服务器突然宕机了,则快照中只保存了T1时刻的快照,在t时间段内的数据修改未被记录(丢失)。如下图:

t时刻宕机,未执行快照

从上图明显可以看出,RDB并不是一个完美的日志记录方案」,只有让t时间逐渐缩小,才能保证丢失的数据缩小。

「那么问题来了,时间能够缩短1秒吗?」 即是每秒执行一次快照。

全量快照是记录某一个时刻的「全部」内存数据,每秒执行一次的对Redis性能影响巨大,于是「增量快照」就出来了。

增量快照

「增量快照是指做了一次全量快照之后,后续的快照只对修改的数据进行快照记录」,这样可以避免每次都全量快照的开销。

增量快照的前提是Redis能够记住修改的数据,这个功能其实开销也是巨大的,需要保存完整的键值对,这对内存的消耗是巨大的。

为了解决这个问题,Redis使用了AOFRDB混合使用的方式。

AOF和RDB混合使用

这个概念是在Redis4.0提出的,简单的说就是「内存快照以一定的频率执行,比如1小时一次,在两次快照之间,使用AOF日志记录这期间的所有命令操作。」

混合使用的方式使得内存快照不必频繁的执行,并且AOF记录的也不是全部的操作命令,而是两次快照之间的操作命令,不会出现AOF日志文件过大的情况了,避免了AOF重写的开销了。

这个方案既能够用到的RDB的快速恢复的好处,又能享受都只记录操作命令的简单优势,强烈建议使用。

总结

RDB内存快照记录的是某一个时刻的内存数据,因此能够快速恢复;AOFRDB混合使用能够使得宕机后数据快速恢复,又能够避免AOF日志文件过大。

总结

本文介绍了两种数据恢复和持久化的方案,分别是AOFRDB

AOF介绍了什么?如下:

  1. AOF是写后日志,通过记录操作命令持久化数据。
  2. 由于AOF是在命令执行之后记录日志,如果在写入磁盘之前服务器宕机,则会丢失数据;如果写入磁盘的时候突然阻塞,则会阻塞主线程;为了解决以上问题,AOF机制提供了三种写回的策略,每种策略都有不同的优缺点。
  3. AOF日志文件过大怎么办?AOF通过fork一个子线程重写一个新的日志文件(共享主线程的内存,记录最新数据的写入命令),同时子线程重写,避免阻塞主线程。

RDB介绍了什么?如下:

  1. RDB是内存快照,记录某一个时刻的内存数据,而不是操作命令。
  2. Redis提供了两个命令,分别是savebgsave来执行全量快照,这两个命令的区别则是save是在主线程执行,势必会阻塞主线程,bgsave是在fork一个子线程,共享内存。
  3. RDB通过操作系统的「写时复制技术」,能够保证在执行快照的同时主线程能够修改快照。
  4. 由于两次快照之间是存在间隔的,一旦服务器宕机,则会丢失两次间隔时刻的数据,Redis4.0开始使用AOF日志记录两次快照之间执行的命令(AOFRDB混合使用)。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
打赏
0
0
0
0
47
分享
相关文章
Redis应用—2.在列表数据里的应用
本文介绍了基于数据库和缓存双写的分享贴功能设计,包括:基于数据库 + 缓存双写的分享贴功能、查询分享贴列表缓存时的延迟构建、分页列表惰性缓存方案、用户分享贴列表数据按页缓存实现精准过期控制、用户分享贴列表的分页缓存异步更新、数据库与缓存的分页数据一致性方案、热门用户分享贴列表的分页缓存失效时消除并发线程串行等待锁的影响。总结:该设计通过合理的缓存策略和异步处理机制,有效提升了系统性能,降低了内存占用,并确保了数据的一致性和高可用性。
Redis应用—2.在列表数据里的应用
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用的算法是哈希槽分区算法。Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分的哈希槽。在对数据进行操作的时候,集群会对使用CRC16算法对key进行计算并对16384取模(slot = CRC16(key)%16383),得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的Redis节点,然后直接到这个对应的节点上进行存取操作
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,
Redis的数据淘汰策略有哪些 ?
Redis 提供 8 种数据淘汰策略: 淘汰易失数据(具有过期时间的数据) 1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 2. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰 3. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 4. volatile-random:从已设置过期
Redis的数据持久化策略有哪些 ?
Redis 提供了两种方式,实现数据的持久化到硬盘。 1. RDB 持久化(全量),是指在指定的时间间隔内将内存中的数据集快照写入磁盘。 2. AOF持久化(增量),以日志的形式记录服务器所处理的每一个写、删除操作 RDB和AOF一起使用, 在Redis4.0版本支持混合持久化方式 ( 设置 aof-use-rdb-preamble yes )
Redis应用—1.在用户数据里的应用
本文主要介绍了社区电商的业务闭环及Redis缓存架构中遇到的典型生产问题及其解决方案。通过介绍的设计和优化,社区电商平台能够在高并发读取和少量写入的情况下,保持高性能和数据一致性。
Redis应用—1.在用户数据里的应用
Redis的数据过期策略有哪些 ?
1. 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断 a. 如果未过期,返回数据 b. 发现已过期,删除,返回nil 2. 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。默认情况下 Redis 定期检查的频率是每秒扫描 10 次,用于定期清除过期键。当然此值还可以通过配置文件进行设置,在 redis.conf 中修改配置“hz”
Redis经典问题:数据并发竞争
数据并发竞争是大流量系统(如火车票系统、微博平台)中常见的问题,可能导致用户体验下降甚至系统崩溃。本文介绍了两种解决方案:1) 加写回操作加互斥锁,查询失败快速返回默认值;2) 保持多个缓存备份,减少并发竞争概率。通过实践案例展示,成功提高了系统的稳定性和性能。
Redis经典问题:数据不一致
在使用Redis时,缓存与数据库数据不一致会导致应用异常。主要原因包括缓存更新失败、Rehash异常等。解决方案有:重试机制、缩短缓存时间、优化写入策略、建立监控报警、定期验证一致性、采用缓存分层及数据回滚恢复机制。这些措施可确保数据最终一致性,提升应用稳定性和性能。
|
5月前
|
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
103 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等