Redis深入系列-0x019:Redis 持久化

0x000 概述


这篇文章概括了关于Redis持久化技术,推荐所有的Redis用户都应该阅读。关于Redis持久化和健壮性保证详细信息可以查看这篇文章-解密Redis持久化

0x001 Redis持久化


Redis提供了一系列不同的持久化选项:

  • RDB持久化可以指定定时器定时捕捉数据集快照。
  • AOF持久化记录每一次服务端收到的写操作,并在服务端重新驱动的时候再次执行,重构原始数据集。命令的记录使用的是Redis本身的命令格式,并且是拼接式的。当它变得太大的时候,Redis可以在后台重写记录。
  • 如果你希望你的数据只是在服务端运行的时候存在,你可以完全关闭持久化。
  • 在同一个实例上将AOFRDB结合起来使用也是可以的。要注意,在这种情况下,Redis重启的时候,将会使用AOF文件来重构原始数据,因为它保证数据的完整性。

明白如何权衡取舍AOFRDB持久化就最重要的。先从RDB开始:
RDB优点:

  • RDB是一个结构紧凑的单文件,他代表你的数据集在某个时间点的快照。RDB文件用来做备份是非常完美的,比如你可以24个小时之内每个小时打包一个RDB文件,并且吧每天RDB快照保存30天。这允许你很简单的就保存数据不同的数据版本,可以很好的用于灾备。
  • RDB对于故障恢复非常好,因为单个结构紧凑的文件可以转移到很远的数据中心,或者在Amazon S3(可能需要加密)
  • RDB使得Redis的性能最大化,因为Redis主线程唯一要做的事就是创建子进程,剩下的事都交给子线程去完成。主线程将不会执行磁盘I/O操作。
  • 相对与AOF来说,当数据集很大的时候,RDB可以很快的重启。

RDB缺点:

  • 如果你希望在Redis突然停止工作的时候丢失的数据最少(比如停电),那么RDB并不好。你可以配置不同的时间点来创建RDB(比如至少5分钟并且有100条写入命令,但你可以有多个保存点)。然而大部分情况下,我们总是每5分钟或者更长一点的时间创建一个RDB镜像,所以,如果Redis因为某些原因,并没有正常的关机,你必须做好丢失最新几分钟的数据的准备。
  • RDB为了使用子进程去完成持久化操作,需要经常调用fork()函数,fork()在数据集比较大的时候是非常耗时的,如果数据集非常多并且CPU性能并不是很好,可能会导致Redis暂停服务几毫秒,甚至1秒。AOF也需要调用fork(),但是你可以不需要权衡的调整多久重写日志。

AOF advantages
AOF的优点:

  • AOF的耐久性更好:你可以有不同的文件同步策略:完全不使用文件同步,每秒都使用文件同步,在每次查询的时候文件同步。默认策略是每秒都使用文件同步,虽然如此,写性能依旧出色,你最多只会丢失1秒的写入数据。
  • APF的日志是追加型的日志,所以在突然停电的时候不会多,也不会少。即使因为某些原因(比如磁盘满了)日志的结尾只写入了半个命令,redis-check-aof工具也可以简单的修复。
  • 如果日志文件太大,Redis可以自动的在后台重写AOF。重写操作也是完全安全的,Redis可以继续追加数据到旧的文件,新的文件将会以最小操作需要去创建当前的数据集,当新文件创建并准备好,Redis就会将两者调换,并开始写入到新的文件。
  • AOF的日志包含了所有的操作,格式良好并且容易理解。你甚至可以很简单的导出AOF文件。比如就算你使用FLUSHALL命令,只要你在这期间没有重写日志文件,你也可以停止你的服务端,保存你的数据,移除最新的命令,然后重启Redis

*AOF disadvantages
AOF缺点

  • 对于同一个数据集,AOF文件总是比RDB的文件大。
  • AOFRDB慢,因为AOF精确的文件同步策略。在通常情况下,每秒的文件同步策略的性能也是非常好的,如果禁止文件同步策略,性能将和RDB一致,就算在高负载之下。RDB在高写入负载下依旧可以提供更好的最高延迟保障,
  • 在过去的时候,在一些特殊的命令上,我们遇到过非常罕见的bug(比如像BRPOPLPUSH这种堵塞命令),导致AOF在重新加载的时候没有产生和原数据集相同的数据集。这种bug是非常罕见的,我们在测试用例中自动创建了随机的复杂的数据集,然后重新加载他们并且检查发现所有的东西都没有问题,但是这种问题在RDB持久化中几乎不可能存在。让这个观点更明确一点:AOF增加更新一个已经存在的状态,就像MysSQL或者MongoDB那样,然而RDB抓取所有的数据并创建快照,这在概念上更加的健壮。然而,一来需要注意的是每一次AOF重写都是从真实数据集的开始抓取并重新创建,出现的bug和追加AOF文件相比更加的麻烦(或者一次重写读取的是旧的AOF文件而不是从内存中读取)。二来在真实世界中吗我们没有接到过一个关于AOF命令重复的报告。

0x0002所以我们应该使用哪个?


通常来说,如果你想要达到PostgreSQL那种数据安全性,我们应该结合两种持久化方式来使用。

如果你比较关心你的数据,但是依旧可以接受几分钟的数据丢失,那你可以只是简单的使用RDB

非常多的用户都只是单独的使用AOF,我们并不鼓励这种做法,因为实时拥有一个RDB快照作为备份是一个非常好的做法,为了更快的重启或者放置AOF引擎的bug。
注意:因为这些原因,在未来,我们可能会终止AOFRDB持久化方式的合并(长的团队计划)。
接下来的章节将会更加详细的解释两种持久化模式。

0x003 快照

默认情况下,Redis将数据集的快照在磁盘上保存成一个名为dump.rdb的文件。你可配置Redis在几秒内数据集有多少变化的时候保存快照,或者你也可以直接的使用SAVEBGSAVE命令。

比如,下面这个配置将会让Redis在60秒内产生1000个key的改变的时候自动创建数据集快照。

save 60 1000

这种策略就是快照。

0x004 他是怎么工作的

每当Redis需要导出数据集到磁盘的时候,将会发生下面的事:

  • Redis调用fork(),就有了一个子进程和主进程
  • 子进程将数据集写入临时的RDB文件
  • 当子进程写入新的RDB文件完成的时候,将会覆盖旧的文件。

这个方法允许Rediscopy-on-write语义中获得好处。可以用复制并写入来形容这个方法。

0x005 只允许追加的文件

快照的持久性并不是很号,如果你的电脑将Redis停了,或者你的生产线停了,或者你意外使用kill -9停止了你的实例,最新写入Redis的数据将会丢失。这对于一些应用来说是一个大问题,他们需要很高的持久性,在这种情况下Redis并没有很好的选项。

可以使用全量持久性策略来替代追加文件策略。这在1.1版本就被支持了。
你可以在你的配置文件中开启:

appendonly yes

从现在开始,每次Redis接收到改变数据集的命令(比如SET),他将会追加数据到AOF。当重启的时候,他将会使用AOF文件重构状态。

0x006 日志重写


就像你猜的,越多的写操作被执行,AOF就会变得越大,比如,如果你将一个计数自增100次,最终在数据集中,将会有一个单独的key,但是在AOF中将存在100条记录,其中99条对于重构当前状态是没有必要的的。
Redis支持一个很有趣的特性:可以不打断当前服务的情况下在后台重构AOF。不论你何时调用BGREWRITEAOFRedis都会将重构当前数据集必要的最短的命令序列写入文件。如果你在Redis 2.2中使用AOF,,你需要时不时执行BGREWRITEAOFRedis 2.4以后可以自动执行日志重写(可以查看2.4的案例配置文件获取更多详细情况)。

0x007 只支持追加文件的健壮性如何?

你可以配置文件同步到磁盘的频率。有三个参数:

  • 当有新的命令被追加到AOF的时候就文件同步一次。非常慢,非常安全。
  • 每秒一次。非常快(在2.4和快照一样快),你在故障的时候只会丢失一秒的数据
  • 从来不同步,这是吧数据交给操作系统,更快但是最不安全的方法。

推荐(默认)的策略是每秒文件同步一次。不但快而且安全。在实践中这种策略总是非常慢的(尽管在2.0中改善了)-没有什么办法可以让他更快了

0x008 AOF出错的时候该怎么错?

如果服务端在写入AOF文件的时候出错了(这从来没有导致不一致),因为某种原因出错让Redis无法加载它。当发生这种事的时候可以使用下列步骤修复:

  • 复制AOF文件
  • 使用redis-check-aof工具修复:
$ redis-check-aof --fix
  • 还可以使用diff -u来对比两个文件的差异
  • 使用修复后的文件重启服务。

0x009 这是怎么工作的

  • 日志重写使用和快照相同的复制并写入的技巧,它是这么工作的:
  • Redis创建进程,所以现在有一个子进程和一份主进程。
  • 子进程开始写入新的AOF临时文件
  • 主进程在内存中缓存新的改变(同时,将新的变化写入旧的文件,所以,如果重写失败了,我们的数据还是安全的)
  • 子进程重写文件完成以后,主进程得到信号,将内存中的缓存追加到子进程生成的文件中。
  • 接着Redis重命名旧的文件为新名字,同时开始追加新数据到新文件。

0x010 如果在使用dump.rdb快照,如何切换到AOF。

这个操作在Redis2.0Redis2.2中是不一样的,就像你猜的,在Redis 2.2中非常简单,并且完全不需要重启。
Redis >= 2.2

  • 备份最新的数据到dump.rdb
  • 将备份文件放到安全的地方
  • 执行下面两个命令

    $ redis-cli config set appendonly yes
    $ redis-cli config set save ""
  • 确保你的数据库包含相同数量的key
  • 确保写入操作被正确追加到只允许追加的文件

第一个CONFIG操作启用Append Only File,为了让Redis阻塞并生生初始化转存,他们将会打开文件并写入,开始追加接下来所有的写入操作。
第二个CONFI命令用来关闭快照持久化。这是可选的,你可以让两个持久化方法都启用
重要:记住修改你的redis.conf文件去启用AOF,否则当你重启服务端的时候配置的改变将会丢失,并且你的服务端将会以旧的配置文件启动。
Redis 2.0

  • 备份最新的数据到dump.rdb
  • 将备份文件放到安全的地方
  • 停止所有的写入操作
  • 调用redis-cli bgrewriteaof。将会创建一个只支持追加的文件
  • Redis生成完成AOF文件的时候停止服务端
  • 编辑redis.conf启用只支持追加文件持久化
  • 重启服务端
  • 确保你的数据库包含相同数量的key
  • 确保写入操作被正确追加到只允许追加的文件

0x011 AOFRDB持久化的相互影响

Redis 2.4以后RDB快照操作执行的时候,不会让AOF重写执行,当AOF重写在执行的时候,也不会让BGSAVE执行,这阻止了两个Redis进程同时做过于繁重的磁盘I/O操作
当快照正在执行的时候,如果用户显式子的调用BGREWRITEAOF,服务端将会回复OK状态码,告诉用户操作已经在日程中,重写将会在快照完成之后执行。

AOFRDB都开启的场景下,Redis重启后将会使用AOF文件重构原始的数据集,因为它更能保证数据完整。

0x012 备份Redis数据

在开始这个章节之前,有些东西必须知道:保证备份你的数据。磁盘损坏,云中的实例消失了或者其他情况:没有备份意味着你将冒着数据放到/dev/null的巨大风险。

Redis对数据备份是非常友好的,因为你可以在数据库运行的时候复制RDB文件:RDB文件一旦创建就不再修改,而且生成的时候使用的是临时的名字,它将在最终目标生成完成的的时候自动重命名。
这意味着当服务端运行的时候复制RDB文件是完全安全的。这是我们的建议:

  • 在你的服务器创建一个cron任务,每小时创建一个RDB快照文件放在一个文件夹,每天创建一个RDB快照文件放在另一个文件夹。
  • 每次执行cron脚本的时候,确保太旧的文件被删除:按小时备份的数据,你可以保存最新的48小时的,按天备份的,你可以保存一道两个月的。确保快照的命名有日期和时间信息。
  • 至少每天一次的确保将RDB快照文件放到你的数据中西或者至少不放在你运行Redis实例的物理服务器上。

0x013 故障恢复

故障恢复在Redis的场景中也是非常基础的操作,就像备份一样,因为他可以吧备份放置到外部的数据中心。在这种情况下,就算在Redis运行并产生快照的主要的数据中心发生一个大突变,数据也是安全的。

因为很多Redis使用者都在初创场景,没有太多的资金,所以我们将会回顾大部分有趣的却不需要耗费大多的故障恢复技术。

  • Amazon S3和其他一些类似的服务商是挂在你的故障恢复系统的号方式。用加密的形式将你每小时或者每天生成的RDB快照传输到S3。确保将你的密码到不同的安全的地方(比如给你组织的其他成员一个备份)。使用多个存储服务商是提高数据安全性是非常值得推荐的。
  • 使用SCP(SSH的一部分),远程传输你的快照。这是一个非常简单却安全的路由:在一个距离你很远的地方买一个VPS并安装SSH,使用key而不是用密码,添加authorized_keys文件到VPS。你就可以自动传输你的备份了,在两个不同服务商购买VPS会更好。

这个系统如果没有以正确的方式编码是很容易失败的。如果你使用VPS,至少绝对保证可以验证传输之后的文件大小(它和你复制的文件应该一样大),或者可以验证SHA1
你当然也需要一些报警系统,为了防止备份传输工作因为某些原因而没有正确的工作

阅读 1.4k

推荐阅读

哎,好像不能申请多个专栏呢,原本这个专栏只放前端文章,现在看来不行了!就都放吧!

22 人关注
111 篇文章
专栏主页