六) redis的持久化机制,为啥不能用redis做专门的持久化数据库存储?

Redis持久化详解
https://www.jianshu.com/p/81421cfa30a7

1)redis的持久化方式
redis的持久化方式有两种rdb(redis数据库快照)和aof(append only file 追加日志)

a)RDB方式
Redis支持将当前数据的快照存成一个数据文件的持久化机制。我们可以通过redis的save命令来配置RDB快照生成的时机(默认的时机是分了1s有多少数据写入或5分钟有多少数据写入或1小时有多少数据写入都会触发)
对于一个持续写入的redis数据库,Rdb方式具体的流程如下:
 redis会fork一个子进程,然后再子进程中循环遍历所有的数据,并将其写入并生成RDB快照,不影响主进程。
 为了提高性能,linux的fork命令实际上利用了COPY_ON_WRITE技术,也就是说对于内存中的值,主进程和子进程共享其内容,如果要发生修改的话,主进程会将修改的内容生成一份副本复制给子进程,也就是写时复制。
 生成的rdb文件总是可用的。因为redis的子进程会先将数据写入一个临时文件中,然后通过原子操作rename系统调用将临时文件重命名为rdb文件,从而保证及时在故障的时候,redis的rdb文件总是可用的(要么是当前最新的rdb文件,要么是旧的rdb文件)。

RDB方式的缺点是一旦数据库出现问题,我们RDB文件中保存的数据并不是最新的,从上次RDB文件生成到redis停机这段时间的数据会全部丢掉。这个对于数据的实时性要求并不高的场合是可以考虑使用的,但是要求高的应用就不适合了。

另外,补充一点,redis的rdb文件也是redis主从同步的内部实现中的重要一环。

b) aof方式
有点类似于mysql主从同步中使用的binlog(二进制文件)。但是aof是可识别的纯文本,它里面存的就是一个个的标准redis写命令。对于每一条写操作,都会生成一条相应的命令作为日志记录下来。

Aof是一个写文件操作,那么它也会遇到写入磁盘的过程中的数据丢失问题。这也就是说,在redis对AOF中调用write(2)写入(系统内核缓存)后,何时再调用fsync命令将其写入到磁盘中。通过appendfsync命令可以控制:

  1. appendfsync no :
    redis不会主动调用fsync命令将AOF日志同步到磁盘,此时依赖于操作系统的调试了。多数LINUX操作系统都是每30s进行一次fsync,将缓冲区数据写到磁盘;
  2. appendfsync everysec:
    redis的默认策略。每隔1s进行一次fsync调用,将缓冲区的数据写到磁盘。这种操作会将1s内的数据批量写入磁盘,性能较好。但是当这一次的fsync调用超出1s时,redis会延时下一次fsync,再等1s,也就是说2s后再进行fsync。这一次的fsync命令不管执行多长时间都会进行。也就是说,这种设置通常情况下会1s一次执行一次fsync;最坏情况下2s执行一次fsync
  3. appednfsync always:每次有写操作都会调用一次fsync,这是数据是最安全的;当然性能也会受影响。

AOF的性能提升
由于每个写命令都会生成一条日志,故AOF文件会很大。Redis额外提供了一个功能,对AOF日志的重写功能。在重新时,会合并对同一数据的多次重写,仅保留最后一条。其生成过程有点类似RDB方式,也是fork一个子进程,直接遍历数据,写入一个临时文件中。在写入临时文件的过程中,redis新执行的写操作还是会继续写到老的aof文件中,同时还会记录到内存缓存区中。当重写操作完成后,再讲缓冲区日志一次性的写入到临时文件中,最后再调用原子性的rename命令用新的aof文件取代老的aof文件。

从上面的流程我们能够看到,RDB和AOF操作都是顺序IO操作,性能都很高。而同时在通过RDB文件或者AOF日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中。所以也不会造成磁盘的随机读。

2)数据持久化到磁盘的过程
https://www.jianshu.com/p/2a9d296c72c7
什么是持久化,简单来讲就是将数据放到断电后数据不会丢失的设备中。也就是我们通常理解的硬盘上。
写操作的流程
首先我们来看一下数据库在进行写操作时到底做了哪些事,主要有下面五个过程。
1.客户端向服务端发送写操作(数据在客户端的内存中)
2.数据库服务端接收到写请求的数据(数据在服务端的内存中)
3.服务端调用write(2) 这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)
4.操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
5.磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)

故障分析
写操作大致有上面5个流程,下面我们结合上面的5个流程看一下各种级别的故障。
当数据库系统故障时,这时候系统内核还是OK的,那么此时只要我们执行完了第3步,那么数据就是安全的,因为后续操作系统会来完成后面几步,保证数据最终会落到磁盘上。
当系统断电,这时候上面5项中提到的所有缓存都会失效,并且数据库和操作系统都会停止工作。所以只有当数据在完成第5步后,机器断电才能保证数据不丢失,在上述四步中的数据都会丢失。
通过上面5步的了解,可能我们会希望搞清下面一些问题:
1.数据库多长时间调用一次write(2),将数据写到内核缓冲区?
2.内核多长时间会将系统缓冲区中的数据写到磁盘控制器?
3.磁盘控制器又在什么时候把缓存中的数据写到物理介质上?
对于第一个问题,通常数据库层面会进行全面控制。而对第二个问题,操作系统有其默认的策略,但是我们也可以通过POSIX API提供的fsync系列命令强制操作系统将数据从内核区写到磁盘控制器上。对于第三个问题,好像数据库已经无法触及,但实际上,大多数情况下磁盘缓存是被设置关闭的。或者是只开启为读缓存,也就是写操作不会进行缓存,直接写到磁盘。建议的做法是仅仅当你的磁盘设备有备用电池时才开启写缓存。

数据损坏
所谓数据损坏,就是数据无法恢复,上面我们讲的都是如何保证数据是确实写到磁盘上去,但是写到磁盘上可能并不意味着数据不会损坏。比如我们可能一次写请求会进行两次不同的写操作,当意外发生时,可能会导致一次写操作安全完成,但是另一次还没有进行。如果数据库的数据文件结构组织不合理,可能就会导致数据完全不能恢复的状况出现。
这里通常也有三种策略来组织数据,以防止数据文件损坏到无法恢复的情况:
第一种是最粗糙的处理,就是不通过数据的组织形式保证数据的可恢复性。而是通过配置数据同步备份的方式,在数据文件损坏后通过数据备份来进行恢复。实际上MongoDB在不开启journaling日志,通过配置Replica Sets时就是这种情况。
另一种是在上面基础上添加一个操作日志,每次操作时记一下操作的行为,这样我们可以通过操作日志来进行数据恢复。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况。这也类似于MongoDB开启了journaling日志的情况。
更保险的做法是数据库不进行老数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了。实际上CouchDB就是此做法的优秀范例。

3)为什么不适合做专门的持久化数据库存储?
1)基于内存,体积受限,不如磁盘
2)选择的持久化方式有rdb(快照)和aof(追加日志),rdb是隔一端时间生成一次快照,很容易丢数据;而aof则可以设置几种模式,常见的是每秒把写记录追加到日志中,也会丢一定的数据;如果采用的是只要有记录就追加到日志,那么性能很差。
3)即使是作为数据库,它缺少分表、schema、索引、外键、Date等常见数据数据库提供的功能,join也很困难

猜你喜欢

转载自blog.csdn.net/xiaohesdu/article/details/87906319
今日推荐