百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Redis系列2:数据持久化提高可用性

nanshan 2024-10-20 07:36 13 浏览 0 评论

1 介绍

从上一篇的 《深刻理解高性能Redis的本质》 中可以知道, 我们经常在数据库层上加一层缓存(如Redis),来保证数据的访问效率。
这样性能确实也有了大幅度的提升,但是本身Redis也是一层服务,也存在宕机、故障的可能性。
一旦服务挂起,可能生产的后果包括如下几方面:
1、Redis的数据是存在内存中的,所以一旦挂起,内存中的数据会全部丢失。
2、I/O从内存层级迁移到磁盘层级,性能极速下降。
3、原本访问缓存的请求会透过缓存层直接投向数据库,给数据库带来极大的压力,甚至导致雪崩。

所以,缓存层崩溃产生的后果是灾难的。为了避免宕机和宕机后的数据丢失, 为了保证数据的快速恢复,Redis提供了两个持久化数据的能力, AOF(Append Only FIle)日志 和 RDB 快照。

2 关于RDB 内存快照

大规模高并发的分布式场景,经常会遇到问题就是Redis挂起,导致访问失败,而所有的请求透过缓存层投向数据库,给数据库造成极大的压力。
而Redis的数据是存储在高速缓存中,即使我们重启并且恢复使用,缓存池依旧是空的,因为内存被释放了。


重新建立缓存的过程,对数据库也是一个暴击的过程,很可能会导致整个系统调用链的雪崩。参考我的这篇《架构与思维:一次缓存雪崩的灾难复盘》
我们知道,Redis 数据都是保存在内存中,能不能将内存中的数据进一步写到磁盘上,Redis 重启的时候就可以把磁盘上的数据快速恢复到内存中。这样,即使Redis宕机重启之后,依然能够正常的提供服务。


但是不能忽略一个问题,Redis和MySQL最大的区别之一就是一个存储在内存,一个持久化在磁盘。但是如果每次数据的变化(新增、修改、删除缓存)都要写内存并同时写磁盘,这样成本太高,内存+磁盘,会让 Redis 性能大大降低。而且还要保证原子性操作,避免内存和磁盘的数据不一致。

2.1 使用内存快照

为了避免实时写入高频操作磁盘带来的负面效应。Redis提供了内存快照策略。
我们知道,Redis 在 执行写(增、删、改)指令过程中,内存中数据会持续的在变化。而内存快照,指的是 Redis 内存中的数据在某一刻的状态。就好比如是拍照一样,你把那一刻的数据都定格下来,持久化到磁盘上。打游戏的同学可以想象存盘。
快照文件我们称之为 RDB 文件,即 Redis DataBase 的缩写。
Redis 通过定时执行 RDB 内存快照,这样就不必每次执行写指令都存盘,只需要在执行内存快照的时候写磁盘。这样既保证Redis的高效读写,还实现了定时持久化,宕机后可快速恢复数据。


如上图,在做数据恢复时,直接将 RDB 文件读入内存完成恢复。

2.2 生成RDB策略

Redis 提供了两种模式来生成 RDB 文件:

  • save: 由主线程来执行,同步阻塞,只有等save完成后,才能进行新操作;
  • bgsave:执行后,会立刻返回OK,同时调用 glibc 的函数fork产生一个子进程用于写入 RDB 文件,快照持久化完全交给子进程来处理。主进程继续执行他自己的工作,非阻塞。

2.2.1 save模式

save模式是主进程执行,非常不建议使用主进程执行的方式,在 《深刻理解高性能Redis的本质》 中,
我们知道他的主操作都是在单线程模型上完成的。所以尽量避免 RDB 文件生成影响主线程的网络I/O和键值对读写。

2.2.2 bgsave模式

上面提到的另外一种方式,fork一个子进程来写RDB文件。
Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化,这个很重要,具体可以了解下这篇《Copy On Write机制》,写的不错。
Redis 在持久化时会调用 glibc 的函数fork产生一个子进程,由这个子进程来处理快照持久化的动作,子进程可以共享主进程的所有内存数据,所以它读取到主进程的数据之后写入到 RDB 文件。而父进程继续处理客户端的写操作,不受影响。
在创建 RDB 文件时,程序会对数据库中的键进行检查,仅仅将未过期的键保存到新创建的 RDB 文件中。
当主进程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读取这个副本数据写到 RDB 文件,所以主进程就可以直接修改原来的数据。


这既保证了快照的完整性,也允许主进程同时对数据进行修改,避免了对正常业务的影响。

2.2.3 避免过频全量照片

虽然说Redis 使用 bgsave 函数 fork 子进程在后台完成 内存中的数据做快照,没有影响父进程继续处理客户端的各种操作。
但是需注意一点,过于频繁的执行全量的数据快照,必然会导致严重的性能开销:

  • 频繁生成 RDB 文件写入磁盘,磁盘压力过大,效率降低。
  • fork 出来的 bgsave 子进程因为共享主线程的数据,一定程度上会阻塞主线程的运行,主线程的内存越大,阻塞时间越长。

2.3 总结

  • 快照的恢复速度快,但是生成 RDB 文件的频率需要把握一个度,频率过低快照间隔数据较大,丢失的数据就会比较多;频率太快,又会消耗额外开销,降低Redis性能。
  • RDB 建议采用二进制 + 数据压缩的方式写磁盘,文件体积小,数据恢复速度快。

3 AOF 日志

AOF 日志存储了 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。
假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令。
也就是说,可以通过重放(replay),来建立 Redis 当前实例的内存数据结构。这种模式有没有很熟悉,有没有想到MySQL主从同步时候的relay log。

3.1 日志变更前后对比

AOF记录日志有两种模式,一种是预写式日志,也称写前日志(Write Ahead Log, WAL): 在实际写数据之前,将修改的数据写到日志文件中。
另外一种是写后日志: 先执行写操作,当数据存入内存后,再记录日志。
预写式日志类似 MySQL Innodb 引擎 中的 redo log,修改数据前先记录日志,再修改。

3.2 日志格式

Redis 接收到 set keyName someValue 命令的时候,会先将数据写到内存,Redis 会按照如下格式写入 AOF 文件。
*3:表示当前指令分为三个部分,每个部分都是 $ + 数字开头,后面是3部分的具体内容:指令、键、值。
数字:表示这部分的命令、键、值多占用的字节大小。比如 $3表示这部分包含 3 个字符,也就是 set 的长度。

推荐使用写后日志的模式,避免了额外的检查开销,不需要对执行的命令进行语法检查。如果使用写前日志的话,就需要先检查语法是否有误,否则日志记录了错误的命令,在使用日志恢复的时候就会出错。另外,写后才记录日志,不会阻塞当前的 指令执行。

# set keyName someValue
*3
$3
set
$7  #长度为7
keyName
$9 #长度为9
someValue

# 执行 mset key1 1 ,key2 2 ,key33 3
# aof日志如下:
*7  # 本批命令需要往下读7行非 $ 开始的命令
$4  #接着读取4个字节宽度,‘mset’长度为4,记为 $4
mset
$4  #接着读取4个字节宽度,‘key1’长度为4,记为 $4
key1
$1  #接着读取1个字节宽度,‘1’长度为1,记为 $1
1
$4
key2
$1
2
$5  #接着读取的字节宽度,‘$key33’长度为5,记为 $5
key33
$1
3

3.3 可能存在的问题

  • 可能存在丢失:比如Redis 刚执行完指令,还没记录日志宕机了,命令数据就丢了。
  • AOF 避免了当前命令的阻塞,但是AOF 日志是主线程执行,将日志写入磁盘过程中,如果磁盘压力大就会导致执行变慢,降低后续的操作。

3.4 写回策略

上面的问题,在Redis高频读写的时候是必然存在的,想要解决,在写入的时候做一层缓冲就可以了,避免直塞。这时候Redis提供了一种执行策略叫写回策略。

3.4.1 写回策略说明

为了提高日志文件的写入效率,写回策略会做如下变化:

  • 当你调用 write 函数将数据写入到文件时,这时候不是真正的落盘,而是将写入数据暂存在操作系统的内存缓冲区里。
  • 待到缓冲区的空间被填满、或者超过了指定的阈值时候,才真正地将缓冲区中的数据写入到磁盘里面。
    这种做法显然提高了效率,但也为写入数据带来了安全性问题,如果服务器发生了单机,那么保存在内存缓冲区里面的写入数据就会丢失。
    为此,系统提供了fsyncfdatasync两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。
    Redis 提供的 AOF 配置项 appendfsync 写回策略直接决定 AOF 持久化功能的效率和安全性,以下是 appendfsync 的3个枚举:
  • always:同步写回,写指令执行完 即将缓冲区内容回写到 AOF 文件。
  • everysec:每秒写回,写指令执行完,日志写到 AOF 文件缓冲区,缓冲区每隔一秒再把内容同步到磁盘。
  • no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时回写到磁盘。

写磁盘会带来性能上的损耗,所以写回的策略要根据实际情况做一个取舍,比如你是偏向性能还是可靠性。
always 同步写回可以做到数据不丢失,但是每次执行写指令都需要写入磁盘,性能最差。
everysec 每秒写回,避免了同步写回的性能开销,但是如果服务发生宕机,会有大约1s时间周期的数据丢失,这种模式是在性能和可靠性之间做了妥协。
no 操作系统控制,执行写指令后就写入 AOF 文件缓冲,再执行后续的写磁盘指令,性能最好,但有可能丢失更多的数据。

3.4.2 写回策略的选择

我们可以根据服务的实际情况来抉择策略,看是偏向高性能还是高可靠。

  • 高性能需求,选择 No 策略
  • 高可靠性保证,就选择 Always 策略
  • 如果能够接受数据存在少量丢失,又希望性能较好的话,就选择 Everysec 策略

4 混合RDF/AOF 方式模式

现实情况下,无论使用RDB或者AOF都差点意思。使用 rdb 来恢复内存状态,势必会丢失一部分数据。 使用 AOF 日志重放,重放对性能有一定的影响,而且在 Redis 实例很大的情况下,需要花费很长的时间。
Redis 4.0 解决了这个问题,才用了一个新的持久化模式——混合持久化,该 混合模式 默认是关闭状态的。
将 RDB 文件的内容和 rdb快照时间点之后的增量的 AOF 日志文件存在一起。这时候 AOF 日志不需要再是全量的日志,而是最近一次快照时间点之后到当下发生的增量 AOF 日志,通常这部分 AOF 日志很小。
所以执行有如下顺序:

  • 查找rdb内容,如果存在先加载 rdb内容再 重放剩余的 aof。
  • 没有rdb内容,直接以aof格式重放整个文件。
    这样快照就不用频繁的执行,同时由于 AOF 只需要记录最近一次快照之后的数据,不需要记录所有的操作,避免了出现单次重放文件过大的问题。

5 总结

  • RDB提供了快照模式,记录某个时间的Redis内存状态。RDB设计了 bgsave 和写时复制,尽可能避免执行快照期间对读写指令的影响,但是频繁快照会给磁盘带来压力以及 fork 阻塞主线程。需把握频率。
  • AOF 日志存储了 Redis 服务的顺序指令序列,通过重放(replay)指令来写入日志文件,并通过写回策略来避免高频读写给Redis带来压力。
  • RDB快照的照片时间间隔,必然会带来数据缺失,如果允许分钟级别的数据丢失,可以只使用 RDB。
  • 如果只用 AOF,写回策略优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。
  • 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择。


作者:翁智华

出处:https://www.cnblogs.com/wzh2010/p/15886790.html

相关推荐

实战派 | Java项目中玩转Redis6.0客户端缓存

铺垫首先介绍一下今天要使用到的工具Lettuce,它是一个可伸缩线程安全的redis客户端。多个线程可以共享同一个RedisConnection,利用nio框架Netty来高效地管理多个连接。放眼望向...

轻松掌握redis缓存穿透、击穿、雪崩问题解决方案(20230529版)

1、缓存穿透所谓缓存穿透就是非法传输了一个在数据库中不存在的条件,导致查询redis和数据库中都没有,并且有大量的请求进来,就会导致对数据库产生压力,解决这一问题的方法如下:1、使用空缓存解决对查询到...

Redis与本地缓存联手:多级缓存架构的奥秘

多级缓存(如Redis+本地缓存)是一种在系统架构中广泛应用的提高系统性能和响应速度的技术手段,它综合利用了不同类型缓存的优势,以下为你详细介绍:基本概念本地缓存:指的是在应用程序所在的服务器内...

腾讯云国际站:腾讯云服务器如何配置Redis缓存?

本文由【云老大】TG@yunlaoda360撰写一、安装Redis使用包管理器安装(推荐)在CentOS系统中,可以通过yum包管理器安装Redis:sudoyumupdate-...

Spring Boot3 整合 Redis 实现数据缓存,你做对了吗?

你是否在开发互联网大厂后端项目时,遇到过系统响应速度慢的问题?当高并发请求涌入,数据库压力剧增,响应时间拉长,用户体验直线下降。相信不少后端开发同行都被这个问题困扰过。其实,通过在SpringBo...

【Redis】Redis应用问题-缓存穿透缓存击穿、缓存雪崩及解决方案

在我们使用redis时,也会存在一些问题,导致请求直接打到数据库上,导致数据库挂掉。下面我们来说说这些问题及解决方案。1、缓存穿透1.1场景一个请求进来后,先去redis进行查找,redis存在,则...

Spring boot 整合Redis缓存你了解多少

在前一篇里面讲到了Redis缓存击穿、缓存穿透、缓存雪崩这三者区别,接下来我们讲解Springboot整合Redis中的一些知识点:之前遇到过,有的了四五年,甚至更长时间的后端Java开发,并且...

揭秘!Redis 缓存与数据库一致性问题的终极解决方案

在现代软件开发中,Redis作为一款高性能的缓存数据库,被广泛应用于提升系统的响应速度和吞吐量。然而,缓存与数据库之间的数据一致性问题,一直是开发者们面临的一大挑战。本文将深入探讨Redis缓存...

高并发下Spring Cache缓存穿透?我用Caffeine+Redis破局

一、什么是缓存穿透?缓存穿透是指查询一个根本不存在的数据,导致请求直接穿透缓存层到达数据库,可能压垮数据库的现象。在高并发场景下,这尤其危险。典型场景:恶意攻击:故意查询不存在的ID(如负数或超大数值...

Redis缓存三剑客:穿透、雪崩、击穿—手把手教你解决

缓存穿透菜小弟:我先问问什么是缓存穿透?我听说是缓存查不到,直接去查数据库了。表哥:没错。缓存穿透是指查询一个缓存中不存在且数据库中也不存在的数据,导致每次请求都直接访问数据库的行为。这种行为会让缓存...

Redis中缓存穿透问题与解决方法

缓存穿透问题概述在Redis作为缓存使用时,缓存穿透是常见问题。正常查询流程是先从Redis缓存获取数据,若有则直接使用;若没有则去数据库查询,查到后存入缓存。但当请求的数据在缓存和数据库中都...

Redis客户端缓存的几种实现方式

前言:Redis作为当今最流行的内存数据库和缓存系统,被广泛应用于各类应用场景。然而,即使Redis本身性能卓越,在高并发场景下,应用于Redis服务器之间的网络通信仍可能成为性能瓶颈。所以客户端缓存...

Nginx合集-常用功能指导

1)启动、重启以及停止nginx进入sbin目录之后,输入以下命令#启动nginx./nginx#指定配置文件启动nginx./nginx-c/usr/local/nginx/conf/n...

腾讯云国际站:腾讯云怎么提升服务器速度?

本文由【云老大】TG@yunlaoda360撰写升级服务器规格选择更高性能的CPU、内存和带宽,以提供更好的处理能力和网络性能。优化网络配置调整网络接口卡(NIC)驱动,优化TCP/IP参数...

雷霆一击服务器管理员教程

本文转载莱卡云游戏服务器雷霆一击管理员教程(搜索莱卡云面版可搜到)首先你需要给服务器设置管理员密码,默认是空的管理员密码在启动页面进行设置设置完成后你需要重启服务器才可生效加入游戏后,点击键盘左上角E...

取消回复欢迎 发表评论: