Redis持久性启发

Redis的一部分,我的工作是阅读博客、论坛信息,twitter“Redis”搜索的时间线。 是非常重要的对于开发人员感觉关于社区的用户,和社区 用户,想想他正在开发的产品。 和我的感觉是没有Redis功能一样误解其持久性。

在这篇文章里我会努力真正公正的:没有Redis的广告,没有试图跳过细节,可能把Redis 坏的灯 。 所有我想要的仅仅是提供一个清晰的、可以理解的照片Redis持久性是如何工作的,可靠是多少,如何与其他的数据库系统。

操作系统和磁盘

首先要考虑的是我们可以从数据库的耐久性。为了这样做,我们可以在一个简单的写操作:想象会发生什么
  • 1:客户端发送一个命令写入数据库(数据在客户端的内存)。
  • 2:数据库接收到写(数据在服务器的内存)。
  • 3:数据库调用系统调用,写到磁盘上的数据(数据是在内核缓冲区)。
  • 4:操作系统传输缓冲区写入磁盘控制器(数据在磁盘缓存)。
  • 5:磁盘控制器实际写数据到物理介质(磁盘、Nand芯片,…)。


注:以上 ,则是将事情过于简单化了 在许多方面,因为有比这更水平的缓存和缓冲区。

步骤2通常实现为一个复杂的内部缓存系统数据库实现,有时写是由一个不同的线程或进程。 但是很快,数据库必须写入数据到磁盘,这是重要的从我们的观点。 也就是说,数据从内存必须传递给内核(步骤3)。

另一个重大遗漏的细节步骤3。 现实更为复杂,因为最先进的内核实现不同层的缓存,通常是文件系统缓存(称为水平 页面缓存 在Linux中)和一个更小的 缓冲区缓存 这是包含数据的缓冲区等待提交到磁盘。 使用特殊的api可以绕过两个(见例如O_DIRECT和O_SYNC开放的旗帜在Linux系统调用)。 不过从我们的观点来看,我们可以认为这是一个独特的层不透明的缓存(也就是说,我们不知道的细节)。 通常足以说,页面缓存时禁用数据库已经实现了它的缓存来避免这两个数据库和内核将试图在同一时间做同样的工作(坏的结果)。 缓冲区缓存通常是永远不会关闭,因为这意味着每个文件将结果写入数据提交到磁盘,太慢了几乎所有的应用程序。

数据库通常做的是调用系统调用将提交缓冲区缓存到磁盘,只有在绝对必要的,稍后我们会看到更详细的。


当我们编写安全的吗?

如果我们考虑失败,包括数据库软件(这个过程被系统管理员或崩溃),不接触内核,写可以被认为是安全与成功,第三步完成后,后写(2)系统调用(或任何其他系统调用用于传输数据到内核)成功返回。 这一步,即使数据库进程崩溃后,仍然内核将照顾将数据转移到磁盘控制器。

如果我们考虑一个更灾难性事件像一个停电,我们只在第五步完成是安全的,也就是说,当数据实际上是转移到物理设备记忆。

我们可以总结,数据安全的重要阶段3、4和5。 那就是:
  • 频率数据库软件将其用户空间缓冲区传输到内核缓冲区使用写(或同等)系统调用?
  • 多久内核缓冲区刷新到磁盘控制器吗?
  • 和磁盘控制器多久会写入数据的物理媒体?


注意:当我们谈论 磁盘控制器 我们实际上意味着执行的缓存控制器 磁盘本身。 耐久性是重要的环境中系统管理员通常禁用这一层的缓存。

磁盘控制器在默认情况下只执行一个为大多数系统编写通过缓存(即只缓存读取,不写)。 使回信是安全模式(写缓存)只有当你有电池或超级电容器设备中的数据保护的电源关闭。


posix api

数据库开发人员的观点的路径实际上遵循之前的数据写入到物理设备很有趣,但更有趣的是 量的控制 编程API提供了路径。

让我们从第3步开始。 我们可以使用 系统调用内核缓冲区传输数据,所以从这个角度看,我们有一个好的控制使用POSIX API。 但是我们没有多少控制对这个系统调用需要多少时间才能返回成功。 内核写缓冲区的大小是有限的,如果不能够处理磁盘应用程序编写带宽,内核写缓冲区将达到它的最大尺寸和内核会阻止我们的写。 当磁盘能够接收更多的数据,编写系统调用最终将返回。 ,毕竟我们的目标是最终达到物理介质。

步骤4:在这个步骤中内核数据转移到磁盘控制器。 默认情况下它将尽量避免这样做,因为它是快将在更大的碎片。 例如Linux默认情况下会提交后写道 30秒 。 这意味着,如果有一个失败,所有的数据写在最近的30秒可以潜在的损失。

POSIX API提供了一个家庭的系统调用来迫使内核缓冲区写入磁盘:家庭可能是最著名的 fsync 系统调用(见也 msync fdatasync 的更多信息)。使用fsync数据库系统能够迫使内核实际上提交数据在磁盘上,但是当你能猜到这是一个很昂贵的操作: fsync将发起一个写操作 每次,有一些数据等待内核缓冲区。 Fsync()也能阻止这一过程所需的时间来完成编写,如果这还不够,在Linux上它也会阻止所有其他线程对同一文件。

我们无法控制

到目前为止,我们了解到,我们可以控制步骤3和4,但是5呢? 正式来说我们没有控制使用POSIX API从这点来看。 也许一些内核实现将试图告诉驱动实际提交物理介质上的数据,或者控制器将再订购写为了速度,并将尽快不是磁盘上写入数据,但是等几毫秒。 这只是我们的控制。

在本文的其余部分,我们将因此简化我们的场景中两个数据安全级别:
  • 数据写入到内核缓冲区使用编写(2)系统调用(或同等)给我们数据安全与过程失败
  • 数据提交到磁盘使用fsync(2)系统调用(或同等)给我们,实际上,数据安全与完整的系统故障像一个停电。 实际上我们知道没有保证,因为可能的磁盘控制器缓存,但我们不会考虑这方面,因为这是一个不变量在所有常见的数据库系统。 系统管理员通常也可以使用专门的工具来控制准确的物理设备的行为。


注意:并不是所有的数据库使用POSIX API。 一些专有的数据库使用一个内核模块,将允许一个更直接的与硬件交互。 然而问题的主要形状是相同的。 您可以使用用户空间缓冲区,或迟内核缓冲区,但很快有写磁盘上的数据,以确保它的安全(这是一个缓慢的操作)。 一个值得注意的例子使用一个内核模块是Oracle数据库。


数据损坏

在前面的段落我们分析的问题确保实际上是转移到磁盘的数据更高水平层:应用程序和内核。 然而这不是唯一担心耐久性。 另一个如下:是数据库仍然可读的灾难性事件后,或其内部结构可以以某种方式破坏了,它可能不再正确读取,或需要复苏步骤以重建的有效表示数据?

例如许多SQL和NoSQL数据库实现某种形式的磁盘上树数据结构,用于存储数据和索引。 这个数据结构是写操作。 如果系统停止工作的写操作,树表示仍然有效吗?

一般有三个层次的安全数据免遭损坏:

  • 写入磁盘的数据库表示不关心发生在出现故障时,要求用户使用数据恢复的复制品,和/或提供工具,会尝试重新建立起一个有效的表示如果可能的话
  • 数据库系统使用的操作日志(日志),以便他们能失败后恢复到一致的状态。
  • 数据库系统不修改已经书面数据,但只有工作只添加模式,腐败是不可能的
现在我们有了我们需要的所有元素评估数据库持久层的系统的可靠性。 是时候看看Redis分数在这方面。Redis提供了两种不同的持久性选项,所以我们会检查一个接一个。

快照

Redis快照是最简单Redis持久性模式。 它产生的时间点快照数据集在满足特定的条件下,例如如果前面创建快照超过2分钟前,已经有至少100个新的写道,创建一个新的快照。 这个条件可以由用户配置Redis控制实例,并在运行时也可以修改而不需要重新启动服务器。 生产作为一个快照 .rdb 文件,其中包含整个数据集。

快照的耐久性是限于用户指定为 节省点 。 如果数据集保存每15分钟,比在发生Redis实例崩溃或灾难性事件,15分钟写可能会丢失。 从的角度Redis交易快照保证一个多/ EXEC事务完全写入一个快照,也不存在(已经说RDB文件代表完全 的时间点 图像的数据集)。

RDB文件不能损坏,因为它是由子进程在一个扩展的方式,从Redis内存中数据的形象。 新rdb快照创建一个临时文件,并获得使用原子到目标文件重命名(2)系统调用一旦成功所产生的子进程(只有在它同步磁盘上使用fsync系统调用)。

Redis快照不提供良好的耐久性担保如果几分钟的数据丢失是不可接受的事件,所以它的使用是有限的应用程序和环境中失去最近的数据不是非常重要的。

但是即使使用Redis提供的更高级的持久性模式,称为“AOF”,它也仍然是明智的快照,因为有一个紧凑的RDB包含整个数据集是非常有用的文件进行备份,将数据发送给远程数据中心灾难恢复,或者轻松地回滚到一个旧版本的数据集在一个戏剧性的软件缺陷,严重地破坏数据库的内容。

值得注意的是,RDB Redis快照也使用在执行主- >从同步。

RDB额外的好处之一是对于给定的数据库大小,系统上的I / o的数量,任何数据库上的活动。这是房地产,大多数传统的数据库系统(和其他持久性、Redis AOF)没有。


只附加文件

只附加文件,通常被称为简单AOF,是主要的Redis持久性选项。它的工作方式非常简单:每一次写操作,执行修改内存中的数据集,操作记录。 使用日志产生完全相同的格式用Redis客户沟通,所以AOF可以通过netcat甚至输送到另一个实例,或者如果需要轻松地解析。 在重启Redis re-plays重建数据集的所有操作。

AOF作品如何在实践中我将做一个简单的实验,建立一个新的Redis 2.6实例仅与附加文件启用:
./redis-server --appendonly yes
现在是时候派几个写命令的实例:
redis 127.0.0.1:6379> set key1 Hello
OK
redis 127.0.0.1:6379> append key1 " World!"
(integer) 12
redis 127.0.0.1:6379> del key1
(integer) 1
redis 127.0.0.1:6379> del non_existing_key
(integer) 0
其实前三个操作修改了数据集,第四个没有,因为没有键指定的名称。 这就是我们的附加文件看起来像:
$ cat appendonly.aof 
*2
$6
SELECT
$1
0
*3
$3
set
$4
key1
$5
Hello
*3
$6
append
$4
key1
$7
 World!
*2
$3
del
$4
key1
正如你所看到的最后DEL丢失,因为它没有产生任何修改数据集。

那么简单,新命令收到会登录到AOF,但前提是他们对实际数据有影响。 然而并不是所有的命令都记录接收到。 例如阻塞操作列表记录的最终影响正常非阻塞的命令。 同样INCRBYFLOAT记录集,使用后的最终值增量作为负载,以便不同的方式浮动点是由不同的体系结构将不会重新加载一个AOF文件后会导致不同的结果。

到目前为止,我们知道Redis AOF只是一个附加业务,所以腐败是不可能的。 然而这个理想的特性也可以一个问题:在上面的例子中德尔操作我们的实例后完全是空的,价值仍然AOF是几个字节的数据。 AOF是一个 总是不断增长的文件 ,所以如何处理当它太大吗?

AOF重写

当一个AOF太大Redis只会从头重写它在一个临时文件。 重写不是由读旧的,但直接访问数据在内存中,以便Redis可以创建最短AOF可以生成,不需要读磁盘访问而写新的。

一旦终止重写,磁盘上的临时文件同步fsync和用于覆盖旧AOF文件。

你可能想知道会发生什么变化的数据写入服务器在重写的进步。 这个新数据也只是写旧(当前)AOF文件,同时到一个内存中的缓冲区中排队,这样当新AOF准备好了我们可以写这里面丢失的部分,并最终取代旧AOF文件与新的。

仍然可以看到一切都只是附加,而当我们重写AOF旧AOF里我们仍然写一切文件,对所有新创建所需的时间。 这意味着我们的分析,我们可以简单地避免考虑到AOF Redis被重写。 所以真正的问题是,我们如何经常写(2),以及我们常常fsync(2)。

AOF重写生成只使用顺序I / O操作,所以整个转储过程高效即使旋转磁盘(不执行随机I / O)。这也适用于RDB快照的一代。缺乏完整的随机I / O访问数据库中是一种罕见的特性,并可能主要是因为Redis服务从内存读取操作,因此数据在磁盘上组织不需要随机访问模式,但就加载顺序启动。


AOF耐久性

整篇文章是达到这一段写的。 我很高兴我在这儿,和你是我更高兴 仍然 在这里和我在一起。

的Redis AOF使用的用户空间缓冲区填充新的数据作为新的命令执行。 缓冲区通常是磁盘上的刷新 每次我们返回到事件循环 ,使用一个写(2)调用AOF文件描述符,但实际上有三种不同的配置,将改变的确切行为写(2),和特别是fsync(2)调用。

这三种配置控制的 appendfsync 配置指令,可以有三个不同的价值观:不,everysec,总是。 这个配置也可以查询或修改在运行时使用的配置命令,这样你就可以改变它每次不停Redis实例。

appendfsync没有

在这个配置Redis不执行fsync(2)调用。 然而,它将确保客户 不使用 流水线 ,客户等待接受命令的回复发送下一个之前,将收到一个承认命令正确执行 只有在改变是通过编写命令转移到内核AOF文件描述符,使用写(2)系统调用

因为在这个配置fsync(2)是不叫,数据将被提交到磁盘在内核的愿望,也就是说,在大多数Linux系统每30秒。

appendfsync everysec

在这个配置数据将被写入到文件使用写(2)和刷新从内核到磁盘使用fsync(2) 每秒钟一次 。 通常写(2)调用会被执行每次我们回到事件循环,但这是没有保证的。

然而如果磁盘不能应对写入速度,和背景fsync(2)调用超过1秒,Redis可能延迟写入一个额外的第二(为了避免编写将阻塞主线程,因为fsync(2)在后台运行的线程对同一文件描述符)。 如果一个共 两秒 运行没有fsync(2)能够终止,Redis最后执行(可能阻塞)写(2)将数据转移到磁盘不惜任何代价。

在这种模式下Redis保证,在最坏的情况下,2秒内你写的一切将是致力于操作系统缓冲区 转移到磁盘。 在平均情况下数据将提交每一秒。

appednfsync总是

在这种模式下,如果客户端不使用流水线,但是等待回复之前发行新命令,数据都写入到磁盘上的文件和同步使用fsync(2) 承认之前返回给客户端

这是你可以得到的最高层次的耐久性,但低于其他模式。

默认Redis配置 appendfsync everysec ,提供了一个良好的平衡速度(几乎一样快 appendfsync没有 )和耐久性。

当appendfsync Redis实现什么准备 总是 通常被称为 组织承诺 。这意味着,而不是使用一个fsync呼吁每一个执行写操作,Redis能够 集团 这个承诺在一个写+ fsync操作之前执行发送请求的客户,发布了一个写操作期间最新的事件循环迭代。

在实践中这意味着你可以拥有数以百计的客户执行写操作在同一时间:fsync操作将factorized——所以即使在这种模式下Redis应该能够支持一千个并发事务的每秒而转动设备只能维持100 - 200写op / s。

这个功能通常是很难实现在传统的数据库中,但Redis使它更简单。


为什么管道不同?

处理客户的原因使用流水线方式不同,客户使用流水线 与写 牺牲是阅读能力与给定的命令,发生了什么事在执行下一个之前,为了获得速度。 是毫无意义的回复客户之前提交数据之前,似乎不感兴趣回复,客户要求的速度。 写道,然而即使客户端使用流水线和fsyncs(根据配置)总是发生在回到事件循环。

AOF和Redis交易

AOF保证正确的多/ EXEC事务语义,并将拒绝重新加载一个文件,其中包含破碎的事务在文件的末尾。 附带的一个实用程序Redis服务器可以修剪AOF文件删除部分事务结束时。

注意:由于AOF文件使用填充一个写(2)调用在每一个事件循环迭代结束时,一个不完整的事务只能出现如果AOF所在的磁盘满而Redis写作。

与PostrgreSQL

那么耐用Redis,其主要持久性引擎(AOF)在其默认配置?
  • 最糟糕的情况:它保证写(2)和fsync(2)执行在两秒。
  • 正常情况下:它执行写(2)之前回复客户,并执行一个fsync(2)每一秒。
有趣的是,在这种模式下Redis仍极快,几方面的原因。 一是fsync执行在一个后台线程,另一个是美国,Redis只写在附加模式,这是一个很大的优势。

但是如果你需要最大的数据安全和写负载不高,你仍然可以有最好的是可能获得的耐久性 在任何数据库系统 使用 fsync总是

相比之下,PostgreSQL,,(有充分的理由)被认为是好,非常可靠的数据库吗?

让我们一起阅读一些PostgreSQL文档(注意,我只是引用了有趣的片段,你可以找到完整的文档 在PostgreSQL的官方网站 )

fsync(布尔)

如果这个参数,PostgreSQL服务器将试图确保更新写入磁盘,通过发行fsync()系统调用或各种等效方法(见wal_sync_method)。 这确保了数据库集群 可以恢复到一个一致的状态在一个操作系统或硬件崩溃

(一)

在许多情况下,关闭synchronous_commit非关键交易可以提供许多潜在的关掉fsync性能优势,没有服务员数据损坏的风险。


所以PostgreSQL需要fsync数据以避免腐败。 幸运的是,Redis AOF我们没有这个问题,腐败是不可能的。 让我们检查下一个参数,这是一个更紧密地与Redis fsync政策,即使名称是不同的:

synchronous_commit(枚举)

指定事务提交是否会等待WAL记录被写入到磁盘之前,命令返回“成功”显示给客户端。 有效值,地方、。默认值,和安全、价值。 之间时,可以有延迟时成功是报告给客户端和事务是保证安全的服务器崩溃。 (最大延迟wal_writer_delay三倍。) fsync不同,设置这个参数不创建任何数据库不一致的风险:一个操作系统或数据库崩溃可能导致一些最近allegedly-committed事务迷路,但数据库状态将是如果这些交易流产干净一样。


在这里我们有很多类似于我们能与Redis格格不入。 基本上PostgreSQL的家伙告诉你,想要速度吗? 可能这是一个好主意来禁用同步提交。这就像在Redis:想要速度吗? 不要使用 appendfsync总是

现在如果你在PostgreSQL禁用同步提交你在事件与Redis非常相似 appendfsync everysec ,因为默认情况下 wal_writer_delay 设置为200毫秒,文档,你需要乘以三个的实际延迟写道,因此600毫秒,非常接近1秒Redis违约。

MySQL InnoDB也有类似的参数,用户可以调整。 从文档:

如果innodb_flush_log_at_trx_commit的值是0,日志缓冲区写入日志文件一次每秒,刷新到磁盘上执行的操作日志文件,但是没有在事务提交完成。 当该值为1(默认),日志缓冲区被写入日志文件在每个事务提交和刷新到磁盘上执行的操作日志文件。 值为2时,日志缓冲区写入文件在每个提交,但不执行刷新到磁盘操作。 然而,在日志文件进行一次冲洗每秒也当的值是2。 注意,once-per-second冲洗也不能100%保证每秒钟发生,由于进程调度问题。

你可以 在这里阅读更多


长话短说:即使Redis是一个在内存中数据库提供良好的耐久性比其他磁盘上的数据库。

从一个更实际的观点来说Redis同时提供AOF和RDB快照,可以同时启用(这是建议设置,有疑问时),同时提供简单的操作和数据持久性。

我们说的一切Redis耐久性也可以应用不仅Redis用作数据存储时,还用于实现队列时,需要保存在磁盘上具有良好的耐久性。

学分

迪迪埃斯培西亚 为这篇文章提供非常有用的思想和见解。 主题是巨大的,我相信我忽视了很多东西,但由于德罗巴目前的文章初稿相比要好得多。

附录:关于重启时间

我收到了一些请求添加一些信息关于重启时间,因为当Redis实例停止并重新启动它必须从磁盘读取数据到内存中。 我认为这是一个很好的补充,因为RDB和AOF持久性之间有差异,和Redis 2.6和2.4 Redis之间。 相比之下也很有意思,看看Redis PostgreSQL和MySQL。

首先值得提及为什么Redis需要在内存中加载整个数据集开始前向客户服务请求:原因不,严格地说,它是一个内存数据库。 可想而知,在内存数据库,但是 使用相同的数据在内存和磁盘上的代表 可以尽快提供数据。

其实真正的原因是,我们优化他们所服务的不同作用域的不同表示:在磁盘上我们有一个紧凑的扩展表示这是不适合随机存取。 记忆我们有最好的表现的快速数据获取和修改。 但这迫使我们执行 转换步骤 在加载。 Redis读取磁盘上的钥匙一个接一个,和编码相同的键和关联值使用内存中表示。

RDB文件这个过程是非常快的几个原因:首先,RDB文件通常更紧凑,二进制,有时甚至在相同的格式编码值的内存(这发生小聚合数据类型编码 ziplists intsets )。

CPU和磁盘速度会有很大不同,但一般来说可以认为Redis服务器将加载一个RDB文件10 ~ 20秒的速度每gb的内存使用,所以加载数据集组成的数以万计的g可以甚至几分钟。

加载一个AOF文件,只是重写服务器需要类似于Redis两次每gb 2.6,但当然,如果很多写到了AOF文件 最新的压实可以花费更长的时间(然而Redis在缺省配置触发自动重写如果AOF大小达到初始大小的200%)。

然而重新启动一个实例通常是不需要在单个服务器的设置,这是一个更好的主意使用复制为了控制转移到新的Redis实例没有服务中断。例如对于一个升级到新版本Redis通常系统管理员将设置Redis实例运行新版本作为奴隶的实例,然后将所有客户端指向新实例,这个实例将变成主人,并将最终关闭旧的。

传统的磁盘上的数据库呢? 他们不需要在内存中加载数据… 或者是吗?基本上他们做得更好比Redis这方面,因为,当你开始一个MySQL服务器再服务请求自第一第二,然而如果数据库和索引文件不再是在操作系统的缓存中正在发生的是什么 冷启动 。 在这种情况下,数据库将工作开始以来,但将会非常缓慢,可能无法应对的速度应用程序请求数据。 我看到这个第一手多次发生。

什么是发生在一个寒冷的重启数据库实际上是吗 阅读 数据从磁盘内存,什么Redis非常类似,但增量。

长话短说:Redis需要一些时间重新启动是否大数据集。 磁盘上的数据库在这方面更好的,但你不能指望他们会表现良好在寒冷的情况下重新启动,如果他们是在负载下很容易看到一个条件在整个应用程序实际上阻止了好几分钟。 另一方面 一次 Redis启动时,它开始全速。  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值