从源码解读redis持久化

为什么需要持久化?

由于Redis是一种内存型数据库,即服务器在运行时,系统为其分配了一部分内存存储数据,一旦服务器挂了,或者突然宕机了,那么数据库里面的数据将会丢失,为了使服务器即使突然关机也能保存数据,必须通过持久化的方式将数据从内存保存到磁盘中。

对于进行持久化的程序来说,数据从程序写到计算机的磁盘的流程如下:

1、客户端发送一个写指令给数据库(此时数据在客户端的内存)

2、数据库接收到写的指令以及数据(数据此时在服务端的内存)

3、数据库发起一个系统调用,把数据写到磁盘(此时数据在内核的内存)

4、操作系统把数据传输到磁盘控制器(数据此时在磁盘缓存中)

5、磁盘控制器执行真正写入数据到物理媒介的操作(如磁盘)

如果只是考虑数据库层面,数据在第三阶段之后就安全了,在这个时候,系统调用已经发起了,即使数据库进程奔溃了,系统调用会继续进行,也能顺利将数据写入到磁盘中。 在这一步之后,在第4步内核会将数据从内核缓存保存到磁盘缓存中,但为了系统的效率问题,默认情况下不会太频繁地执行这个动作,大概会在30s执行一次,这就意味着如果这一步失败了或者就在进行这一步的时候服务器突然关机了,那么就可能会有30s的数据丢失了,这种比较普通的灾难性问题也是需要考虑的。

POSIX API也提供了一个系统调用让内核强制将缓存数据写入到磁盘中,比较常见的就是fsync系统调用。

int fsync(int fd);

fsync函数只对由文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束后才返回。每次调用fsync时,会初始化一个写操作,然后把缓冲区的数据写入到磁盘中。fsync()函数在完成写操作的时候会阻塞进程,如果其他线程也在写同一个文件,它也会阻塞其他线程,直到完成写操作。

持久化

持久化是将程序数据在持久状态和瞬时状态间转换的机制。对于程序来说,程序运行中数据是在内存的,如果没有及时同步写入到磁盘,那么一旦断电或者程序突然奔溃,数据就会丢失了,只有把数据及时同步到磁盘,数据才能永久保存,不会因为宕机影像数据的有效性。而持久化就是将数据从程序同步到磁盘的一个动作过程。

Redis的持久化

redis有RDB和AOF两种持久化方式。RDB是快照文件的方式,redis通过执行SAVE/BGSAVE命令,执行数据的备份,将redis当前的数据保存到*.rdb文件中,文件保存了所有的数据集合。AOF是服务器通过读取配置,在指定的时间里,追加redis写操作的命令到*.aof文件中,是一种增量的持久化方式。

RDB

RDB文件通过SAVE或BGSAVE命令实现。 SAVE命令会阻塞Redis服务进程,直到RDB文件创建完成为止。 BGSAVE命令通过fork子进程,有子进程来进行创建RDB文件,父进程和子进程共享数据段,父进程继续提供读写服务,子进程实现备份功能。BGSAVE阶段只有在需要修改共享数据段的时候才进行拷贝,也就是COW(Copy On Write)。SAVE创建RDB文件可以通过设置多个保存条件,只要其中一个条件满足,就可以在后台执行SAVE操作。

SAVE和BGSAVE命令的实现代码如下:

void saveCommand(client *c) {

// BGSAVE执行时不能执行SAVE

if (server.rdb_child_pid != -1) {

addReplyError(c,"Background save already in progress");

return;

}

rdbSaveInfo rsi, *rsiptr;

rsiptr = rdbPopulateSaveInfo(&rsi);

// 调用rdbSave函数执行备份(阻塞当前客户端)

if (rdbSave(server.rdb_filename,rsiptr) == C_OK) {

addReply(c,shared.ok);

} else {

addReply(c,shared.err);

}

}

/*

* BGSAVE 命令实现 [可选参数"schedule"]

*/

void bgsaveCommand(client *c) {

int schedule = 0;

/* 当AOF正在执行时,SCHEDULE参数修改BGSAVE的效果

* BGSAVE会在之后执行,而不是报错

* 可以理解为:BGSAVE被提上日程

*/

if (c->argc > 1) {

// 参数只能是"schedule"

if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) {

schedule = 1;

} else {

addReply(c,shared.syntaxerr);

return;

}

}

// BGSAVE正在执行,不操作

if (server.rdb_child_pid != -1) {

addReplyError(c,"Background save already in progress");

} else if (server.aof_child_pid != -1) {

// aof正在执行,如果schedule==1,BGSAVE被提上日程

if (schedule) {

server.rdb_bgsave_scheduled = 1;

addReplyStatus(c,"Background saving scheduled");

} else {

addReplyError(c,

"An AOF log rewriting in progress: can't BGSAVE right now. "

"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever "

"possible.");

}

} else if (rdbSaveBackground(server.rdb_filename,NULL) == C_OK) {// 否则调用rdbSaveBackground执行备份操作

addReplyStatus(c,"Background saving started");

} else {

addReply(c,shared.err);

}

}

有了RDB文件之后,如果服务器关机了,或者需要新增一个服务器,重新启动数据库服务器之后,就可以通过载入RDB文件恢复之前备份的数据。 但是bgsave会耗费较长时间,不够实时,会导致在停机的时候丢失大量数据。

AOF(Append Only File)

RDB文件保存的是数据库的键值对数据,AOF保存的是数据库执行的写命令。

AOF的实现流程有三步:

append->write->fsync

append追加命令到AOF缓冲区,write将缓冲区的内容写入到程序缓冲区,fsync将程序缓冲区的内容写入到文件。 当AOF持久化功能处于开启状态时,服务器每执行完一个命令,就会将命令以协议格式追加写入到redisServer结构体的aof_buf缓冲区,具体的协议这里不展开阐述。

AOF的持久化发生时期有个配置选项:appendfsync。该选项有三个值: always:所有内容写入并同步到aof文件 everysec:将aof_buf缓冲区的内容写入到AOF文件,如果距离上次同步AOF文件的 no:将aof_buf缓冲区中的所有内容写入到AOF文件,但并不对AOF文件进行同步,由操作系统决定何时进行同步,一般是默认情况下的30s。

AOF持久化模式每个写命令都会追加到AOF文件,随着服务器不断运行,AOF文件会越来越大,为了避免AOF产生的文件太大,服务器会对AOF文件进行重写,将操作相同key的相同命令合并,从而减少文件的大小。

举个例子,要保存一个员工的名字、性别等信息:

> hset employee_12345 name "hoohack"

> hset employee_12345 good_at "php"

> hset employee_12345 gender "male"

只是录入这个哈希键的状态,AOF文件就需要保存三条命令,如果还有其他,比如删除,或者更新值的操作,那命令将会更多,文件会更大,有了重写后,就可以适当地减少文件的大小。

AOF重写的实现原理是先服务器中的数据库,然后遍历数据库,找出每个数据库中的所有键对象,获取键值对的键和值,根据键的类型对键值对进行重写。比如上面的例子,可以合并为下面的一条命令:

> hset employee_12345 name "hoohack" good_at "php" gender "male"。

AOF的重写会执行大量的写入操作,Redis是单线程的,所以如果有服务器直接调用重写,服务器就不能处理其他命令了,因此Redis服务器新起了单独一个进程来执行AOF重写。

Redis执行重写的流程:

在子进程执行AOF重写时,服务端接收到客户端的命令之后,先执行客户端发来的命令,然后将执行后的写命令追加到AOF缓冲区中,同时将执行后的写命令追加到AOF重写缓冲区中。 等到子进程完成了重写工作后,会发一个完成的信号给服务器,服务器就将AOF重写缓冲区中的所有内容追加到AOF文件中,然后原子性地覆盖现有的AOF文件。

RDB和AOF的优缺点

RDB持久化方式可以只通过服务器读取数据就能加载备份中的文件到程序中,而AOF方式必须创建一个伪客户端才能执行。

RDB的文件较小,保存了某个时间点之前的数据,适合做灾备和主从同步。

RDB备份耗时较长,如果数据量大,在遇到宕机的情况下,可能会丢失部分数据。另外,RDB是通过配置使达到某种条件的时候才执行,如果在这段时间内宕机,那么这部分数据也会丢失。

AOF方式,在相同数据集的情况下,文件大小会比RDB方式的大。

AOF的持久化方式也是通过配置的不同,默认配置的是每秒同步,最快的模式是同步每一个命令,最坏的方式是等待系统执行fsync将缓冲同步到磁盘文件中,大部分操作系统是30s。通常情况下会配置为每秒同步一次,所以最多会有1s的数据丢失。

怎样的同步方式更好?

RDB和AOF方式结合。起一个定时任务,每小时备份一份服务器当前状态的数据,以日期和小时命名,另外起一个定时任务,定时删除无效的备份文件(比如48小时之前)。AOF配置为1s一次。这样一来,最多会丢失1s的数据,同时如果redis发生雪崩,也能迅速恢复为前一天的状态,不至于停止服务。

总结

Redis的持久化方案也不是一成不变的,纸上的理论还需要结合实践成果来证明其可行性。

(0)

相关推荐

  • 一篇文章揭秘Redis的磁盘持久化机制

    前言 Redis 是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将 Redis 中的数据以数据或命令的形式从内存保存到本地磁盘.当下次 Redis 重启时,利用持久化文件进行数据恢复.Redis 提供了 RDB 和 AOF 两种持久化机制,前者将当前的数据保存到磁盘,后者则是将每次执行的写命令保存到磁盘(类似于 MySQL 的 Binlog).本文将详细介绍 RDB 和 AOF 两种持久化方案,包括操作方法和持久化的实现原理. 正文 Redis 是一个基于 K-

  • 浅谈redis内存数据的持久化方式

    一.概述 Redis的强大性能很大程度上都是因为所有数据都是存储在内存中的,然而当Redis重启后,所有存储在内存中的数据将会丢失,在很多情况下是无法容忍这样的事情的.所以,我们需要将内存中的数据持久化!典型的需要持久化数据的场景如下: 将Redis作为数据库使用: 将Redis作为缓存服务器使用,但是缓存miss后会对性能造成很大影响,所有缓存同时失效时会造成服务雪崩,无法响应. 本文介绍Redis所支持的两种数据持久化方式. 二.Redis数据持久化 Redis支持两种数据持久化方式:RDB

  • redis持久化的介绍

    1. RDB 1.1 RDB简介 RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里. 工作机制:每隔一段时间,就把内存中的数据保存到硬盘上的指定文件中. RDB是默认开启的! Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件.整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对

  • Linux下redis的持久化、主从同步与哨兵详解

    1.0 redis持久化 Redis是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题,Redis提供了两种持久化的方案,将内存中的数据保存到磁盘中,避免数据的丢失. 1|1RDB持久化 redis提供了RDB持久化的功能,在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)这个功能可以将redis在内存中的的状态保存到硬盘中,RDB持久化产生的RDB文件是一个经过压缩的二进制文件,这个文件被保存在硬盘中,redis可以通过这个文件

  • 内存型数据库Redis持久化小结

    因为Redis是内存型数据库,所以为了防止因为系统崩溃等原因导致数据丢失的问题,Redis提供了两种不同的持久化方法来将数据存储在硬盘里面,一种方法是快照(RDB),它可以将存在于某一个时刻的所有数据都写入到硬盘里面,另外一种方法是只追加文件(AOF),它会在执行写命令时,将被执行的写命令都写入到硬盘里面. 快照持久化 Redis可以通过创建快照来获得在内存里面的数据在某一个时间点上的副本.在创建快照之后,用户可以对快照进行备份,可以将快照复制到其它服务器从而创建具有相同数据的服务器副本,还可以

  • redis的2种持久化方案深入讲解

    前言 Redis是一种高级key-value数据库.它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富.有字符串,链表,集 合和有序集合.支持在服务器端计算集合的并,交和补集(difference)等,还支持多种排序功能.所以Redis也可以被看成是一个数据结构服务 器. Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为"半持久化模式"):也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为&q

  • redis数据的两种持久化方式对比

    一.概念介绍 redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Apend Only File). RDB方式 RDB方式是一种快照式的持久化方法,将某一时刻的数据持久化到磁盘中. •redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件.正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的. •对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而

  • Redis教程(十):持久化详解

    一.Redis提供了哪些持久化机制: 1). RDB持久化:     该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘.        2). AOF持久化:     该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的.     3). 无持久化:     我们可以通过配置的方式禁用Redis服务器的持久化功能,这样我们就可以将Redis视为一个功能加强版的memcached了.     4).

  • Redis两种持久化方案RDB和AOF详解

    本文主要针对Redis 有两种持久化方案RDB和AOF做了详细的分析,希望我们整理的内容能够帮助大家对这个两种方案有更加深入的理解. Redis 有两种持久化方案,RDB (Redis DataBase)和 AOF (Append Only File).如果你想快速了解和使用RDB和AOF,可以直接跳到文章底部看总结.本章节通过配置文件,触发快照的方式,恢复数据的操作,命令操作演示,优缺点来学习 Redis 的重点知识持久化. RDB 详解 RDB 是 Redis 默认的持久化方案.在指定的时间

  • 从源码解读redis持久化

    为什么需要持久化? 由于Redis是一种内存型数据库,即服务器在运行时,系统为其分配了一部分内存存储数据,一旦服务器挂了,或者突然宕机了,那么数据库里面的数据将会丢失,为了使服务器即使突然关机也能保存数据,必须通过持久化的方式将数据从内存保存到磁盘中. 对于进行持久化的程序来说,数据从程序写到计算机的磁盘的流程如下: 1.客户端发送一个写指令给数据库(此时数据在客户端的内存) 2.数据库接收到写的指令以及数据(数据此时在服务端的内存) 3.数据库发起一个系统调用,把数据写到磁盘(此时数据在内核的

  • 深入了解Java线程池:从设计思想到源码解读

    目录 为什么需要线程池 线程池设计思路 线程池的工作机制 线程池的参数及使用 线程池的状态 提交任务 任务队列 线程工厂 拒绝策略 关闭线程池 Executors 静态工厂 合理地配置线程池 线程池的监控 源码分析 execute addWorker Worker runWorker getTask processWorkerExit 面试题 为什么需要线程池 我们知道创建线程的常用方式就是 new Thread() ,而每一次 new Thread() 都会重新创建一个线程,而线程的创建和销毁

  • mybatis源码解读之executor包懒加载功能 

    ProxyFactory是创建代理类的工厂接口,其中的setProperties方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置.createProxy方法用来创建一个代理对象 public interface ProxyFactory {   // 设置工厂属性   default void setProperties(Properties properties) {   }   // 创建代理对象   Object createProxy(O

  • go 熔断原理分析与源码解读

    目录 正文 熔断原理 熔断器实现 hystrixBreaker和googlebreaker对比 源码解读 结束语 正文 熔断机制(Circuit Breaker)指的是在股票市场的交易时间中,当价格的波动幅度达到某一个限定的目标(熔断点)时,对其暂停交易一段时间的机制.此机制如同保险丝在电流过大时候熔断,故而得名.熔断机制推出的目的是为了防范系统性风险,给市场更多的冷静时间,避免恐慌情绪蔓延导致整个市场波动,从而防止大规模股价下跌现象的发生. 同样的,在高并发的分布式系统设计中,也应该有熔断的机

  • Redisson分布式锁的源码解读分享

    目录 前言 前置知识 分布式锁的思考 Redis订阅/发布机制 Redisson 加锁 订阅 解锁 看门狗 前言 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).Redisson有一样功能是可重入的分布式锁.本文来讨论一下这个功能的特点以及源码分析. 前置知识 在讲Redisson,咱们先来聊聊分布式锁的特点以及Redis的发布/订阅机制,磨刀不误砍柴工. 分布式锁的思考 首先思考下,如果我们自己去实现一个分布式锁,这个锁需要具备

  • Redisson公平锁的源码解读分享

    目录 前言 公平锁 加锁 解锁 总结 前言 我在上一篇文章聊了Redisson的分布式锁,这次继续来聊聊Redisson的公平锁.下面是官方原话: 它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程.所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒. 源码版本:3.17.7 这是我 fork 的分支,添加了自己理解的中文注释:https://g

  • [转]prototype 源码解读 超强推荐第1/3页

    复制代码 代码如下: Prototype is a JavaScript framework that aims to ease development of dynamic web applications. Featuring a unique, easy-to-use toolkit for class-driven development and the nicest Ajax library around, Prototype is quickly becoming the codeb

  • Bootstrap源码解读网格系统(3)

    源码解读Bootstrap网格系统 工作原理 数据行(.row)必须包含在容器(.container)中,以便为其赋予合适的对齐方式和内距(padding).如: <div class="container"> <div class="row"></div> </div> .container的实现源码: .container { padding-right: 15px; padding-left: 15px; mar

  • Ajax::prototype 源码解读

    AJAX之旅(1):由prototype_1.3.1进入javascript殿堂-类的初探  还是决定冠上ajax的头衔,毕竟很多人会用这个关键词搜索.虽然我认为这只是个炒作的概念,不过不得不承认ajax叫起来要方便多了.所以ajax的意思我就不详细解释了. 写这个教程的起因很简单:经过一段时间的ajax学习,有一些体会,并且越发认识到ajax技术的强大,所以决定记录下来,顺便也是对自己思路的整理.有关这个教程的后续,请关注http://www.x2design.net 前几年,javascri

  • Bootstrap源码解读按钮(5)

    源码解读Bootstrap按钮 按钮组 按钮组和下拉菜单组件一样,需要依赖于bootstrap.js.使用"btn-group"的容器,把多个按钮放到这个容器中.例如:<div class="btn-group">...</div> "btn-group"容器里除了可以使用<button>元素之外,还可以使用其他标签元素,比如<a>标签.不过这里面的标签元素需要带有类名".btn"

随机推荐