关于Redis我的一部分工作是阅读博客,论坛以及twitter时间线(time line)。对于开发者来说,能够了解用户社区,非用户社区如果理解他正在开发的产品是非常重要的。据我所知,持久化特性是最易产生误解的Redis特性。

这篇博客中,我会尽力客观公正,不为Redis做宣传,不忽略可能让Redis出丑的诸多细节。我所期望的仅是说明Redis持久化机制,可靠性,以及和其他数据库系统相比较的优势。

操作系统与磁盘

首先需要考虑的是数据库的耐用性(durability)。为此,我们看看一个简单的写操作实现过程:

1. 客户端发送一个写入命令到数据库(数据在客户端内存中)

2. 数据库收到这个写入命令(数据在数据库内存中)

3. 数据库调用系统调用将数据写到磁盘上(数据在内核的buffer中)

4. 操作系统将写Buffer转交到磁盘控制器(数据在磁盘cache中)

5. 磁盘控制器实际将数据写入物理媒质(磁盘或者闪存)

注:上面仅是一个简化过程,实际上存在更多级的缓存。

实际数据库实现中,步骤2常常是一个复杂的缓存系统,有时写入是由不同线程或者进程处理的。然后数据库最终会将数据写入磁盘,以我们的角度看,这一点是有意义的地方。那就是,数据将从内存传送给内核(步骤3) - 或许理解为从用户空间到内核空间更合适一点。

另一个容易忽略细节的步骤3。更复杂的是大多数内核实现了不同层次的缓存,即文件系统级缓存(如linux中的页缓存)和一个更小的包含等待提交给磁盘的数据的buffer缓存。使用特定API可以跳过这两个缓存(如Linux的open系统调用中的O_DIRECT和O_SYNC标志),但以我们的角度看,我们可以将之视为一个不透明的缓存层(我们不知道细节)。当数据库实现了缓存,那么去使能page缓存以避免数据库和内核同时进行缓存就足够了。buffer缓存通常要打开,否则每次文件写入都要提交给磁盘,对于大多数应用来说这太慢了。

数据库通常要做的是调用系统调用,系统调用提交buffer缓存到磁盘。

什么时候我们的写操作是安全的

如果我们仅仅考虑数据库软件错误(进程被杀死或者崩溃)而不触及内核,那么写操作在成功执行完步骤3就可以认为是安全的。即write系统调用(或者其他系统调用)成功返回后。执行完这一步,即使数据库软件崩溃,内核也会负责将数据写入到磁盘控制器。

如果我们继续考虑更严重的事件,如断电,那么只有在执行完步骤5才是安全的,即当数据已经实际写入到物理设备。

我们可以认为最重要的步骤就是步骤3,4,5. 即:

数据库软件从用户空间传输数据到内核空间的频率

内核将buffer中数据写到磁盘控制器的频率

最后是磁盘控制器将数据写入物理设备的频率

注:

当我们讨论磁盘控制器时,我们实际是指磁盘控制器或者磁盘自己的缓存。在耐用性比较重要的场景,系统管理员通常去使能这一层的缓存。

对大多数系统来说,磁盘控制器默认只执行write through(只缓存读操作)。只有在有电池或者超级电容器进行断电保护的时候,激活write back模式(缓存写操作)才是安全的。

POSIX API

从数据库开发者的角度来看,我们感兴趣的是数据实际写入物理设备的过程,但最关注的是API在写入过程中所能提供的控制。

我们从步骤3开始,我们能够使用write系统调用将数据传递到内核buffer,所以我们的角度看,我们使用POSIX API尽可能的控制了数据的写入。然而,我们不能控制这个系统调用在成功返回之前所耗费的时间。内核buffer大小有限如果。如果磁盘不能应对应用程序的写入带宽要求,内核写buffer将会被耗尽,内核将会阻塞写入。当磁盘可以接收更多数据时,write系统调用才会返回。最终要实现的目标是将数据写入物理设备。

步骤4:在这一步,内核将数据传递到磁盘控制器。默认情况下,内核会尽量减少传递数据的频率,因为传递大数据块会更高效。例如,Linux默认会在write调用后30秒钟将数据提交到磁盘控制器。这意味着,如果在此期间出现失败,所有最近30秒写入的数据可能丢失。

POSIX API提供了一组系统调用强制内核将buffer中数据写入到磁盘:最有名的可能就是fsync系统调用(也可参考msync和fdatasync)。fsync为数据库系统提供了一种强制内核将数据写入磁盘的方法,但是你能想象的到,这代价不菲。只要内核buffer中有数据,每次调用fsync发起一次些操作。fsync将会阻塞调用进程,直至所有写入操作完成,在Linux系统中,如果耗时过长,其他对同一文件进行写入的线程也会被阻塞。

我们不能控制的

到目前为止,我们可以控制步骤3和4,那么步骤5呢?正式的回应是,我们不能使用POSIX API控制这一步。或许,某些内核实现将尽力告知驱动器将数据提交到物理设备,但控制器也可能为了优化写入小了对写入操作重新排序,不会立即将数据写入磁盘而是再等待几毫秒。这时我们无能为力。

在文章后面部分,我们将我们的应用场景简化为两个数据安全级别

使用write系统调用的写入的数据对进程失败是安全的

使用fsync系统调用的是对系统失败(如断电)安全的。实际上,我们知道犹豫控制器缓存我们不能保证这一点,但由于所有数据库系统都有这种问题,所以我们不考虑。此外系统管理员也常常使用特定工具以控制物理设备的行为。

注:不是所有数据库都使用POSIX API。一些私有数据库使用内核模块对硬件进行更多直接的控制。但是问题的主要特征保持不变。你可以使用用户空间buffer,内核buffer,并最终会将数据写入到磁盘以保证数据安全(是一个慢操作)。一个典型的使用内核模块的数据库是Oracle。

数据损坏:

在前一节,我们分析了系统高层(应用程序和内核)将数据写入到磁盘保障数据安全的问题。然而这仅仅是数据耐用性的一面。另外一点是:数据库(包括系统)失败后,数据库是否可读,或者其内部结构是否损坏导致数据不能正确读取,或者需要恢复工具重建数据。

例如,许多SQL和NoSQL数据库实现了某种形式的树数据结构存储数据和索引。这一数据结构将会在写操作时被修改。如果系统在写操作过程中停止工作,这一树数据结构是否仍旧正确。

一般来说,对数据损坏有三种层次的安全性:

数据库写入数据时不关心失败,让用户使用数据备份进行数据恢复,或者提供工具尽力重建数据。

数据库系统使用log操作以便在失败场景时能够重新恢复数据

数据库不修改已经写入的数据,而是仅以追加模式工作,所以不会发生数据损坏。

现在,我们了解所有评估数据库系统持久化层可靠性的因素。我们可以看看Redis的表现如何。Redis提供了两种不同的持久化选择,下面依次说明。

快照

Redis快照是最简单的持久化模式。它在某些条件满足时在某个时间点生成快照,如前一次快照2分钟后且至少有100个新的写入操作。这些条件可以通过用户配置文件实现,可以在不重启服务器的条件下修改。快照是一个单个.rdb文件,包含整个数据集。

快照的耐用性只能限定在用户指定的存储点。如果数据集每15分钟后存一次,那么在Redis实例崩溃或者更严重事件发生时,那么15分钟的写入将会丢失。从Redis事务的角度看,快照能够保证MULTI/EXEC事务可以完全写入快照,或者不写入。

RDB文件不会损坏,因为它是由一个子进程以追加的方式生成。新的rdb快照是一个临时文件,生成成功后,使用原子系统调用rename将其修改为最终文件。

Redis快照不能提供好的耐用性保证,因为在两次快照中间的可能多达几分钟的数据丢失是不可接受的,它只对不关注丢失最新数据的应用和场景适用。

然而,即使是使用另外一种更高级持久化模式-AOF时,仍旧建议打开快照功能,因为它提供的一个包含完整数据集的文件在进行数据备份时,将数据发送给其他数据中心进行灾难恢复,或者在重大软件错误严重损坏数据时进行数据回滚益处多多。

需要注意的是,Redis快照Redis使用快照实现主从同步。

仅追加文件(AOF)

仅追加文件或者简称为AOF,是主要的Redis持久化选项。它的工作方式很简单:内存中每一次修改数据集的写操作都会被写入日志。日志的格式和客户端同Redis之间的通信格式相同。因此AOF能够使用netcat经管道传递给另外一个Redis实例,或者说需要时易于解析。Redis重启时,通过重放所有操作重建数据集。

为了说明AOF如何实际工作我们做一个简单的严重,启动一个新的Redis 2.6实例并激活AOF模式:

./redis-server --appendonly yes

现在发送一个写命令给这个Redis实例。

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

前三个操作实际修改了数据集,第四个没有修改,它只是试图删除一个不存在的key。下面是AOF文件的内容

$ 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文件。

Redis AOF仅仅时做追加操作,不会有数据损坏。但是问题是,AOF文件会不断增加,及时数据集已经被删除为空,当文件很大时怎么办呢?

Redis persistence demystified - part 1的更多相关文章

  1. Redis persistence demystified

    https://redis.io/topics/persistence http://oldblog.antirez.com/post/redis-persistence-demystified.ht ...

  2. Redis persistence demystified - part 2

    重写AOF 当AOF文件太大时,Redis将在临时文件重新写入新的内容.重写不会读取旧的AOF文件,而是直接访问内存中数据,以便让新产生的AOF文件最小,重写过程不需要读取磁盘. 重写完成后,Redi ...

  3. redis 持久化与备份策略 【转载】

    本文转载自 http://blog.csdn.net/is_zhoufeng/article/details/10210353 持久化(persistence) 本文是 Redis 持久化文档 的中文 ...

  4. redis 持久化与备份策略

    持久化(persistence) 本文是 Redis 持久化文档 的中文翻译. 这篇文章提供了 Redis 持久化的技术性描述,推荐所有 Redis 用户阅读. 要更广泛地了解 Redis 持久化,以 ...

  5. Redis 持久化深入--机制、可靠性及比较

    本文是对 antirez 博客中 Redis persistence demystified 的翻译和总结.主要从Redis的持久化机制,提供何种程度的可靠性以及与其他数据库的比较三个方面进行讨论. ...

  6. 详解redis持久化

    我们的Redis必须使用数据持久化吗?如果我们的Redis服务器只作为缓存使用,Redis中存储的所有数据都是从其他地方同步过来的备份,那么就没必要开启数据持久化的选项.Redis提供了将数据定期自动 ...

  7. Redis(7)——持久化【一文了解】

    一.持久化简介 Redis 的数据 全部存储 在 内存 中,如果 突然宕机,数据就会全部丢失,因此必须有一套机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的 持久化机制, ...

  8. 转载:解密Redis持久化

    本文内容来源于Redis作者博文,Redis作者说,他看到的所有针对Redis的讨论中,对Redis持久化的误解是最大的,于是他写了一篇长文来对Redis的持久化进行了系统性的论述.文章非常长,也很值 ...

  9. 【Redis】Redis 持久化之 RDB 与 AOF 详解

    一.Redis 持久化 我们知道Redis的数据是全部存储在内存中的,如果机器突然GG,那么数据就会全部丢失,因此需要有持久化机制来保证数据不会一位宕机而丢失.Redis 为我们提供了两种持久化方案, ...

随机推荐

  1. SAS Annotated Output GLM

    SAS Annotated Output GLM   在使用SAS过程中,proc glm步输出离差平方和有4种算法,分别是SS1 SS2 SS3 SS4 下面文章介绍了其中SS3的具体计算步骤和例子 ...

  2. 14 Using Indexes and Clusters

    do not build indexes unless necessary. 索引是非常占资源的To maintain optimal performance, drop indexes that a ...

  3. Python学习(7)数字

    目录 Python 数字 Python 数字类型转换 Python 数学函数 Python 随机数函数 Python 三角函数 Python 数学常量 Python 数字 Python 数字数据类型用 ...

  4. JSP连接数据库的两种方式:Jdbc-Odbc桥和Jdbc直连(转)

    学JSP的同学都要知道怎么连数据库,网上的示例各有各的做法,弄得都不知道用谁的好.其实方法千变万化,本质上就两种:Jdbc-Odbc桥和Jdbc直连. 下面先以MySQL为例说说这两种方式各是怎么连的 ...

  5. Android控件_使用TextView实现跑马灯效果

    一.第一种方式: 通过TextView控件的自身属性实现(但是有缺点就是当多个TextView要实现这种效果的时候,只有第一个才有效果)  实现方法加上下面四个属性: android:singleLi ...

  6. HTML的<body>标签详解与HTML常用的控制标记

    一.<body>标签: 用于标记网页的主体,body 元素包含文档的所有内容(比如文本.超链接.图像.表格和列表等等.) 1.body标签中可用的属性: bgcolor="颜色值 ...

  7. 多线程调用HttpWebRequest并发连接限制

    .net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 windows xp , windows  7 下默认是2,在服务器操作 ...

  8. PHP 安全相关 简单知识

    概要: 1.php一些安全配置 (1)关闭php提示错误功能 (2)关闭一些“坏功能” (3)严格配置文件权限. 2.严格的数据验证,你的用户不全是“好”人 2.1为了确保程序的安全性,健壮性,数据验 ...

  9. Mysql 组合查询 UNION 与 UNION ALL

  10. phalcon: model 验证数据完整性

    The above example performs a validation using the built-in validator “InclusionIn”. It checks the va ...