《Redis作者:深度剖析Redis持久化》要点:
本文介绍了Redis作者:深度剖析Redis持久化,希望对您有用。如果有疑问,可以联系我们。
近日,Redis的作者在博客中写到,他看到的所有针对Redis的讨论中,对Redis持久化的误解是最大的,于是他写了一篇长文来对Redis的持久化进行了系统性的论述.
文章主要包括三个方面:Redis持久化是如何工作的、这一性能是否可靠以及和其它类型的数据库比较.以下为文章内容:
一、Redis持久化是如何工作的?
什么是持久化?简单来讲就是将数据放到断电后数据不会丢失的设备中,也就是我们通常理解的硬盘上.
首先我们来看一下数据库在进行写操作时到底做了哪些事,主要有下面五个过程:
客户端向服务端发送写操作(数据在客户端的内存中).
1 The client sends a write command to the database (data is in client's memory)
数据库服务端接收到写哀求的数据(数据在服务端的内存中).
2: The database receives the write (data is in server's memory).
服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中).
3 The database calls the system call that writes the data on disk (data is in the kernel's buffer).
操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中).
4: The operating system transfers the write buffer to the disk controller (data is in the disk cache)
磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上).
5: The disk controller actually writes the data into a physical media (a magnetic disk, a Nand chip, ...).
故障分析
写操作大致有上面5个流程,下面我们结合上面的5个流程看一下各种级别的故障:
当数据库系统故障时,这时候系统内核还是完好的.那么此时只要我们执行完了第3步,那么数据就是平安的,因为后续操作系统会来完成后面几步,保证数据最终会落到磁盘上.
当系统断电时,这时候上面5项中提到的所有缓存都会失效,并且数据库和操作系统都会停止工作.所以只有当数据在完成第5步后,才能保证在断电后数据不丢失.
通过上面5步的了解,可能我们会希望搞清下面一些问题:
1 数据库多长时间调用一次write,将数据写到内核缓冲区?
2 内核多长时间会将系统缓冲区中的数据写到磁盘控制器?
3 磁盘控制器又在什么时候把缓存中的数据写到物理介质上?
对于第一个问题,通常数据库层面会进行全面控制.
Let's start from step 3. We can use the write system call to transfer data to the kernel buffers,
so from this point of view we have a good control using the POSIX API.
However we don't have much control about how much time this system call will take before returning successfully.The kernel write buffer is limited in size,
if the disk is not able to cope with the application write bandwidth, the kernel write buffer will reach it's maximum size and the kernel will block our write.
When the disk will be able to receive more data, the write system call will finally return.
After all the goal is to, eventually, reach the physical media.
kernel buffers 有限可能满 此时会block
第二个问题,操作系统有其默认的策略,
但是我们也可以通过POSIX API提供的fsync系列命令强制操作系统将数据从内核区写到磁盘控制器上.
For instance Linux by default will actually commit writes after 30 seconds.
This means that if there is a failure, all the data written in the latest 30 seconds can get potentially lost
系统默认30秒写一次,如果这30秒内单机造成数据lost
第三个问题
好像数据库已经无法触及
What we can't control
So far we learned that we can control step 3 and 4, but what about 5?
Well formally speaking we don't have control from this point of view using the POSIX API
不能通过代码方式来控制了
但实际上,大多数情况下磁盘缓存是被设置关闭的,或者是只开启为读缓存,也就是说写操作不会进行缓存,直接写到磁盘.建议的做法是仅仅当你的磁盘设备有备用电池时才开启写缓存.
Note: when we talk about disk controller we actually mean the caching performed by the controller or the disk itself. In environments where durability is important system administrators usually disable this layer of caching.
Disk controllers by default only perform a write through caching for most systems (i.e. only reads are cached, not writes). It is safe to enable the write back mode (caching of writes) only when you have batteries or a super-capacitor device protecting the data in case of power shutdown. 系统的电池来控制
数据损坏(Data corruption)
所谓数据损坏,就是数据无法恢复,上面我们讲的都是如何保证数据是确实写到磁盘上去,但是写到磁盘上可能并不意味着数据不会损坏.比如我们可能一次写哀求会进行两次不同的写操作,当意外发生时,可能会导致一次写操作安全完成,但是另一次还没有进行.如果数据库的数据文件结构组织不合理,可能就会导致数据完全不能恢复的状况出现.
这里通常也有三种策略来组织数据,以防止数据文件损坏到无法恢复的情况:
第一种是最粗糙的处理,就是不通过数据的组织形式保证数据的可恢复性.而是通过配置数据同步备份的方式,在数据文件损坏后通过数据备份来进行恢复.实际上MongoDB在不开启操作日志,通过配置Replica Sets时就是这种情况.
另一种是在上面基础上添加一个操作日志,每次操作时记一下操作的行为,这样我们可以通过操作日志来进行数据恢复.因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况.这也类似于MongoDB开启了操作日志的情况.
更保险的做法是数据库不进行旧数据的修改,只是以追加方式去完成写操作,这样数据自己就是一份日志,这样就永远不会出现数据无法恢复的情况了.实际上CouchDB就是此做法的优秀范例.
1、Redis的第一个持久化策略:RDB快照 Snapshotting
触发方式翻译有遗留就是多久触发一次
Redis snapshotting is the simplest Redis persistence mode. It produces point-in-time snapshots of the dataset when specific conditions are met, for instance if the previous snapshot was created more than 2 minutes ago and there are already at least 100 new writes, a new snapshot is created.
This conditions can be controlled by the user configuring the Redis instance, and can also be modified at runtime without restarting the server.
Snapshots are produced as a single .rdb file that contains the whole dataset
Redis支持将当前数据的快照存成一个数据文件的持久化机制.而一个持续写入的数据库如何生成快照呢.Redis借助了fork命令的copy on write机制.在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件.
我们可以通过Redis的save指令来配置RDB快照生成的时机,
比如你可以配置当10分钟以内有100次写入就生成快照,
也可以配置当1小时内有1000次写入就生成快照,也可以多个规则一起实施.
这些规则的定义就在Redis的配置文件中,
你也可以通过Redis的CONFIG SET命令在Redis运行时设置规则,不需要重启Redis.
Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的,当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件,这样在任何时候出现故障,Redis的RDB文件都总是可用的.
同时,Redis的RDB文件也是Redis主从同步内部实现中的一环.
但是,我们可以很明显的看到,
RDB有它的不足,就是一旦数据库出现问题,那么我们的RDB文件中保留的数据并不是全新的,从上次RDB文件生成到 Redis停机这段时间的数据全部丢掉了.
在某些业务下,这是可以忍受的,我们也保举这些业务使用RDB的方式进行持久化,
因为开启RDB的代价并不高. 但是对于另外一些对数据平安性要求极高的应用,无法容忍数据丢失的应用,RDB就无能为力了,所以Redis引入了另一个重要的持久化机制:AOF日志.
2、Redis的第二个持久化策略:AOF日志 Append only file
AOF日志的全称是Append Only File,从名字上我们就能看出来,它是一个追加写入的日志文件.与一般数据库不同的是,AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令.比如我们进行如下实验,使用Redis2.6 版本,在启动命令参数中设置开启AOF功能:
代码
./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
这时我们查看AOF日志文件,就会得到如下内容:
代码
$ cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
key1
$5
Hello
*3
可以看到,写操作都生成了一条相应的命令作为日志.
其中值得注意的是最后一个del命令,它并没有被记录在AOF日志中,这是因为Redis判断出 这个命令不会对当前数据集做出修改.所以不需要记录这个无用的写命令.
另外AOF日志也不是完全按客户端的哀求来生成日志的,比如命令 INCRBYFLOAT 在记AOF日志时就被记成一条SET记录,因为浮点数操作可能在不同的系统上会不同,所以为了避免同一份日志在不同的系统上生成不同的数据集,所以这里只将操作后的结果通过SET来记录.
有产生新的问题了 aof越来越大怎么办!
我就建议是移动dump一次移动当前aof文件到历史记录
However this desirable feature can also be a problem:
in the above example after the DEL operation our instance is completely empty, s
till the AOF is a few bytes worth of data.
The AOF is an always growing file,
so how to deal with it when it gets too big?
AOF重写
你可以会想,每一条写命令都生成一条日志,那么AOF文件是不是会很大?答案是肯定的,AOF文件会越来越大,所以Redis又提供了一个功能,叫做AOF rewrite.
其功能就是重新生成一份AOF文件,新的AOF文件中一条记录的操作只会有一次,
而不像一份老文件那样,可能记录了对同一个值的多次操作.
其生成过程和RDB类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件.
在写入新文件的过程中,所有的写操作日志还是会写到本来老的 AOF文件中,同时还会记录在内存缓冲区中.
当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中.
然后调用原子性的rename命令用新的 AOF文件取代老的AOF文件.
二、Redis持久化性能是否可靠?
而AOF是一个写文件操作,其目的是将操作日志写到磁盘上,所以它也同样会遇到我们上面说的写操作的5个流程.那么写AOF的操作平安性又有多高呢?实际上这是可以设置的,
在Redis中对AOF调用write写入后,何时再调用fsync将其写到磁盘上,
通过appendfsync选项来控制,
下面appendfsync的三个设置项,平安强度逐渐变强.
1、appendfsync no
当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了.对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上.
2、appendfsync everysec
当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘.但是当这一 次的fsync调用时长超过1秒时.Redis会采取延迟fsync的策略,再等一秒钟.也就是在两秒后再进行fsync,这一次的fsync就不管会执行多长时间都会进行.这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞.
所以,结论就是:在绝大多数情况下,Redis会每隔一秒进行一次fsync.在最坏的情况下,两秒钟会进行一次fsync操作.
这一操作在大多数数据库系统中被称为group commit,就是组合多次写操作的数据,一次性将日志写到磁盘.
3、appednfsync always
当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最平安的,当然,由于每次都会执行fsync,所以其性能也会受到影响.
对于pipelining有什么不同?
对于pipelining的操作,
其具体过程是客户端一次性发送N个命令,然后等待这N个命令的返回结果被一起返回.
通过采用pipilining 就意味着放弃了对每一个命令的返回值确认.
由于在这种情况下,N个命令是在同一个执行过程中执行的.
所以当设置appendfsync为everysec 时,
可能会有一些偏差,因为这N个命令可能执行时间超过1秒甚至2秒(执行失败怎么办)
但是可以保证的是,最长时间不会超过这N个命令的执行时间和.
用事物来控制吧
AOF and Redis transactions
AOF guarantees a correct MULTI/EXEC transactions semantic, and will refuse to reload a file that contains a broken transaction at the end of the file. An utility shipped with the Redis server can trim the AOF file to remove the partial transaction at the end.
Note: since the AOF file is populated using a single write(2) call at the end of every event loop iteration, an incomplete transaction can only appear if the disk where the AOF resides gets full while Redis is writing.
三、和其它数据库的比较
上面操作系统层面的数据平安我们已经讲了很多,其实,不同的数据库在实现上都大同小异.总之,最后的结论就是,在Redis开启AOF的情况下,其单机数据平安性并不比这些成熟的SQL数据库弱.
在数据导入方面的比较
这些持久化的数据有什么用,当然是用于重启后的数据恢复.Redis是一个内存数据库,无论是RDB还是AOF,都只是其保证数据恢复的措施.所以 Redis在利用RDB和AOF进行恢复的时候,都会读取RDB或AOF文件,重新加载到内存中.相对于MySQL等数据库的启动时间来说,会长很多,因为MySQL原来是不需要将数据加载到内存中的.
但是相对来说,MySQL启动后提供服务时,其被拜访的热数据也会慢慢加载到内存中,通常我们称之为预热,而在预热完成前,其性能都不会太高.而Redis的好处是一次性将数据加载到内存中,一次性预热.这样只要Redis启动完成,那么其提供服务的速度都是非常快的.
而在利用RDB和利用AOF启动上,其启动时间有一些差别.RDB的启动时间会更短,原因有两个,一是RDB文件中每一条数据只有一条记录,不会像 AOF日志那样可能有一条数据的多次操作记录.所以每条数据只需要写一次就行了.另一个原因是RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不需要再进行数据编码工作.在CPU消耗上要远小于AOF日志的加载.
英文原文:Redis persistence demystified
通过对比英文和中文 现在翻译不是信达雅了 只翻译好的概念,
作者提到的问题 如何办理的不翻译 因此阅读完毕让人更加迷糊
我不清楚翻译人员是如何想的 还是机器翻译一遍 然后本身修改一下
欢迎参与《Redis作者:深度剖析Redis持久化》讨论,分享您的想法,维易PHP学院为您提供专业教程。