当前位置: 首页 > 科技观察

面试中经常被问到的Redis持久化和恢复怎么解决?

时间:2023-03-20 21:43:15 科技观察

一、前言本文主要讲Redis的持久化相关功能。持久化一直是影响Redis性能的高发问题,在面试中也经常被问到。包括RDB相关的具体情况和优缺点,AOF的优缺点,其实由于RDB数据的实时性问题,目前使用较多的是AOF,持久化恢复也首选AOF。RDB是老模型了,现在基本都用AOF了。当然,今天两人要一起谈。二、RDBRDB流程图:RDB的特点:1、RDB是快照模式,即保存key-value数据内容。2、RDB有两种持久化模式,同步save模式和异步bgsave模式。由于save是同步的,所以可以保证数据的一致性,而bgsave则不能。3.保存可以在客户端显式触发,也可以在关机时自动触发;bgsave可以在客户端显式触发,也可以通过配置定时任务触发,也可以在slave节点上触发。4.save导致redis同步阻塞,基本已经弃用。bgsave不会造成阻塞,但也有缺点:fork时,需要增加内存服务器开销,因为当内存不够时,会使用虚拟内存,导致Redis运行阻塞。因此,需要保证有足够的空闲内存。5.默认执行shutdown时,如果没有开启AOF,会自动执行bgsave。6.每个RDB文件被替换。关于优化:Redis会对RDB文件进行压缩,使用LZF算法使得最终的RDB文件远小于内存大小,默认开启。但是消耗CPU。RDB的缺点:1.无法在秒级持久化。2.旧版Redis无法兼容新版RDB。RDB优点:1.文件紧凑,适用于备份和全量复制场景。比如每6小时执行一次bgsave,保存到文件系统等。2、Redis加载RDB和恢复数据的速度比AOF快很多。3、AOF由于RDB的实时数据问题,AOF(appendonlyfile)是目前Redis持久化的主流方式。AOF的特点:1.默认的文件名是appendonly.aof。和RDB一样,存放在配置中的dir目录下。2、AOF与RDB相比,每次写命令都保存下来,数据更实时。3、由于AOF每次都会记录写命令,文件会很大,所以需要优化一下,称为“重写机制”(下文详述)。4、AOF保存的每一条写命令,都放在一个缓冲区中,根据不同的策略同步到磁盘(下面详细介绍)。“Rewrite机制”详解:1.fork子进程(类似bgsave)2.主进程会写入2个buffer,一个是原来的“AOFbuffer”,一个是专门为子进程准备的“AOFrewrite”processWritebuffer”;3.子进程写入新的AOF文件,分批次,默认32m;写入完成后通知主进程。4.主进程将“AOF重写缓冲区”中的数据写入新的AOF文件5.用新的AOF文件替换旧文件改写流程图:buffer同步策略,由参数appendfsync控制,共3种:1.always:调用系统fsync函数,直到同步到硬盘并返回;严重影响redis性能。2.everysec:先调用OSwrite函数,写入buffer,然后redis每秒执行一次OSfsync函数,推荐使用该方法3.no:只执行写OS功能,具体硬盘同步策略由操作系统决定;不推荐,数据不安全,容易丢失数据。4、持久化恢复AOF和RDB文件都可以在服务器重启时进行数据恢复。具体过程如下:从图中可以看出,AOF先加载,没有AOF时才加载RDB。当AOF或RDB出错时,加载失败。5、故障排查和性能优化Redis持久化是影响Redis性能的高发问题,也是面试中经常被问到的问题。1、fork操作Redis在进行RDB或者AOF改写时,必须进行fork操作。对于OS来说,fork是一个重量级的操作。此外,fork还会复制一些数据。虽然不会复制主进程的所有物理空间,但是会复制主进程的空间内存页表。对于一个10GB的Redis进程,大约需要复制20MB的内存页表,所以fork操作的耗时与进程的总内存密切相关。另外,如果使用虚拟化技术,比如Xen虚拟机,fork会比较耗时。一次正常的分叉大约需要20毫秒。为什么呢,假设一个Redis实例的OPS超过5万,如果fork操作耗时秒级,那么上万条命令的执行就会变慢,对生产环境影响很大。我们可以在Infostats统计中查询latestforkusec指标,获取最近一次fork操作所花费的时间,单位为微秒。如何优化:1.优先使用有效支持fork的物理机或虚拟化技术,避免使用Xen。2、控制redis实例的最大内存,尽量控制在10GB以内。3、合理配置linux内存分配策略,避免内存不足导致fork失败。4.降低fork频率,比如适度放宽AOF自动触发的时机,避免不必要的全量复制。2、子进程开销fork完成后,会创建一个子进程。子进程负责RDB或者AOF的重写。这部分流程主要涉及CPU、内存、硬盘的优化。1、CPU写文件的过程是一个CPU密集型过程。通常子进程对单核CPU的利用率接近90%。如何优化?既然是CPU密集型操作,就不要绑定单核CPU,因为会和父CPU竞争。另外,不要与其他CPU密集型服务在同一台机器上。如果部署了多个Redis实例,尽量保证一次只有一个子进程执行重写工作。2、内存子进程是通过fork操作产生的,与父进程占用相同的内存大小。理论上需要两倍的内存才能完成持久化操作,但是Linux有写时复制机制,父子进程会共享同一个物理内存页。当一个进程处理一个写操作时,它会创建一个对应的待修改页面的副本,子进程在fork操作时会共??享整个父进程的内存快照。即——如果在重写过程中有内存修改操作,则父进程负责创建修改内存页的副本。这就是内存消耗的来源。如何优化?尽量保证同一时间只有一个子进程在工作;写入大量数据时避免重写。3、硬盘硬盘开销分析:子进程的主要职责是将RDB或AOF文件写入硬盘进行持久化,这必然会对硬盘造成压力。可以使用iostat、iotop等工具分析硬盘负载。如何优化:1、不要和其他硬盘负载高的服务放在同一台机器上,比如MQ、存储。2、AOFrewrite会消耗大量的硬盘IO,可以开启并配置no-appendfsync-on-rewrite,默认关闭。表示AOF重写时没有进行fsync操作。3、启用AOF的Redis在高并发场景下,如果使用普通机械硬盘,每秒写入速率约为100MB。这时候Redis的性能瓶颈在硬盘上,推荐使用SSD。4、对于单机配置多个Redis实例的情况,可以配置不同的实例将AOF文件存储在不同的磁盘上,以分担硬盘的压力。3、AOF附加阻塞开启AOF持久化后,常见的硬盘同步策略是“每秒同步一次”everysec,用于平衡性能和数据安全。对于这种方式,redis使用另外一个线程每秒进行一次fsync同步硬盘,当系统资源繁忙时,会导致Redis主线程阻塞。流程图如下:从上图我们可以发现,everysec配置最多可能会丢失2秒的数据,而不是1秒;如果系统fsync慢,会导致Redis主线程阻塞,影响效率。问题定位:1、AOF阻塞时,会进入日志。用于记录AOFfsync阻塞导致Redis服务变慢的行为。2.每当发生AOF附加阻塞事件时,aofdelayedfsync指标会累积在infoPersistence统计中。查看该指标,方便定位AOF阻塞问题。3、AOF同步运行最多延迟2秒。当出现延迟时,说明硬盘有性能问题。可以通过监控工具iotop查看,定位消耗IO的进程。4、单机多实例部署Redis单线程架构无法充分利用多核CPU。通常的做法是在一台机器上部署多个实例。当多个实例启用AOF时,它们之间会发生CPU和IO竞争。如何解决这个问题呢?让所有实例的AOF串行执行。我们通过infoPersistence中关于AOF的信息编写一个shell脚本,然后串行执行实例的AOF持久化。整个过程如图:通过不断判断AOF的状态,手动进行AOF重写,保证AOF不会出现竞争。具体的shell编写和info信息判断,可以看下图:6.总结本文主要讲的是Redis的持久化相关功能。持久化一直是影响Redis性能的高发问题,在面试中也经常被问到。包括RDB相关的具体和优缺点,AOF的优缺点,其实由于RDB数据的实时性问题,AOF目前用的比较多。而持久化恢复也是AOF的优先级。持久化问题排查起来很麻烦,但是也就那么几个方面,比如fork耗时,子进程的CPU、内存、硬盘开销,AOF的同步阻塞,单机多实例部署.这些优化可以通过上面写的分析来检查。