您的位置:首页 > 数据库 > Redis

Redis持久化

2016-06-18 23:59 423 查看
众所周知,Redis是内存数据库,且使用单个线程来处理命令请求。它将自己的数据库状态(非空数据库以及它们的键值对)存储在内存里面。所以如果没有持久化机制,不把数据保存到硬盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。

为了解决这个问题,redis 提供两种方法进行数据持久化,分别是RDB和AOF。RDB可以将Redis在内存中的数据库状态保存到磁盘里面以实现持久化,AOF通过记录写命令达到持久化效果。两种方法都有各自的优点,需要我们在生产环境中依照实际的业务情况进行裁定。服务器按照下面的流程图选择持久化方式:




RDB 持久化

RDB持久化既可以手动执行,也可以依据配置文件选项定期执行。RDB持久化生成的RDB文件是一个二进制文件,通过此二进制文件能够还原数据库状态。

如图:





RDB提供两种方式生成RDB文件,分别通过执行SAVE和BGSAVE命令生产RDB文件。SAVE由服务器进程直接执行保存,它会阻塞服务器的进程。BGSAVE是服务器子进程执行保存操作,它的运行不会阻塞服务器进程的运行。


SVAE 命令

上面已经说过,SAVE命令执行时会阻塞服务器的运行。因此,在服务器执行SAVE命令时,客户端发送的所有命令请求都会被拒绝。只有服务器执行完SAVE命令,服务器才会重新接收客户端发送的命令请求。


BGSAVE 命令

与SAVE相反,BGSAVE由服务器子进程执行,在执行时依然可以接收客户端发送命令并处理。但是,服务器处理SAVE、BGSAVE、BGREWRITEAOF三种方式和平时有些不一样。

对于SAVE,在执行BGSAVE时,客户端发送的SAVE命令会被服务器拒绝。服务器禁止SAVE和BGSAVE命令同时执行是为了防止父进程(服务器进程)和子进行同时执行产生竞争条件

对于BGSAVE。在执行BGSAVE时,客户端发送的BGSAVE命令时同样也是被拒绝。原因是为了防止两个BGSAVE产生竞争条件。

对于BGREWRITEAOF。在执行BGSAVE时,也是被拒绝的。原因是因为BGSAVE和BGREWRITEAOF不能同时执行。


定期持久化原理

在陈述原理之前,先来看下几个与定期持久化密切相关的属性。

saveparams属性

在服务启动后,服务器会读取save的值赋给saveparams。save 是
redis.conf
中的一个配置文件,它在服务器中的默认配置为:

save 900 1       //服务器在900秒之内,对数据库进行了至少1次修改。
save 300 10           //服务器在300秒之内,对数据库进行了至少10次修改。
save 60 10000         //服务器在60秒之内,对数据库进行了至少10000次修改。


服务器状态redisServer中saveparams结构如下:

struct redisServer {

// ...

// 记录了保存条件的数组
struct saveparam *saveparams;

// ...
};

struct saveparam {

// 秒数
time_t seconds;

// 修改数
int changes;
};


如果服务器中SAVE属性为默认配置,那么服务器中的状态将会下面这样的。



dirty计数器和lastsave属性 

除了saveparams数组之外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性:

dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入、删除、更新等操作)。

lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。

dirty和lastsave在服务器状态中的结构如下:

struct redisServer {

// ...

// 修改计数器
long long dirty;

// 上一次执行保存的时间
time_t lastsave;

// ...
};


上面已经介绍完几个重要的属性了,现在开始切入正题了。

如果未开启AOF功能,那么在Redis启动后,Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,它的其中一项工作就是检查save选项所设置的保存条件是否已经满足。如果满足的话,就执行BGSAVE命令。下面是ServerCron函数的逻辑代码
def serverCron():
# …
# 遍历所有保存条件
for saveparam in server.saveparams:
# 计算距离上次执行保存操作有多少秒
save_interval = unixtime_now()-server.lastsave

# 如果数据库状态的修改次数超过条件所设置的次数
# 并且距离上次保存的时间超过条件所设置的时间
# 那么执行保存操作
if      server.dirty >= saveparam.changes and \
save_interval > saveparam.seconds:

BGSAVE();
# ...


举个例子,如果Redis服务器的当前状态如下图所示



那么当时间来到1378271101,也即是1378270800的301秒之后,服务器将自动执行一次BGSAVE命令,因为saveparams数组的第二个保存条件——300秒之内有至少10次修改——已经被满足。

假设BGSAVE在执行5秒之后完成,那么上图所示的服务器状态将更新为下图所示的状态,其中dirty计数器已经被重置为0,而lastsave属性也被更新为1378271106。



以上就是Redis服务器根据save选项所设置的保存条件,自动执行BGSAVE命令,进行间隔性数据保存的实现原理。


AOF 持久化

AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。AOF持久化命令可以分为命令追加(append)、文件写入、文件同步三个步骤。开启AOF持久化的配置 
appendonly yes


命令追加

resist服务器中的结构是这样的

struct redisServer {
// ...

// AOF缓冲区
sds aof_buf;

// ...
}


服务器在执行一个写命令后,会将命令追加到 aof_buf 缓冲区的末尾。


命令的写入

将命令写入到aof文件中去


将aof文件中的命令同步到磁盘


appendfsync配置说明

appendfsync no (同步操作交给数据库)

当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。

appendfsync everysec (每隔一秒执行一次同步操作)

当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。但是当这一次的fsync调用时长超过1秒时。Redis会采取延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就不管会执行多 长时间都会进行。这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞。

结论就是,在绝大多数情况下,Redis会每隔一秒进行一 次fsync。在最坏的情况下,两秒钟会进行一次fsync操作。这一操作在大多数数据库系统中被称为group commit,就是组合多次写操作的数据,一次性将日志写到磁盘。


appendfsync always (每一次写操作都会执行同步操作)

置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到响。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: