- 虽然网络上关于redis持久化的相关内容数不胜数,但是一来作为我的学习笔记,好记性不如烂笔头;二来除去官方redis之外,很多有意义的修改或补充都非常值得讨论,所以我想做一下记录。
- 持久化用于重启后的数据恢复,而持久化的引入导致了redis可能产生的性能抖动
- redis持久化的 两种方法
- RDB方式
- RDB方式是redis的默认持久化方式
- 快照RDB持久化过程:
- redis调用fork,产生子进程
- 父进程继续接受用户请求;子进程负责将内存内容写入临时文件,写入完成后rename为dump文件,实现替换
- 在子进程写内存内容期间,父进程如果要修改内存数据,os将会通过写时复制为父进程创建副本,所以此时子进程写入的仍然是fork时刻的整个数据库内容
- 不足之处在于:
- 如果redis出现问题崩溃了,此时的rdb文件可能不是最新的数据,从上次RDB文件生成到redis崩溃这段时间的数据全部丢掉。
- 产生快照时,redis最多将占用2倍于现有数据规模的内存,因此当内存占用过多时,RDB方式可能导致系统负载过高,甚至假死。(有个说法是,当redis的内存占用超过物理内存的3/5时,进行RDB主从复制就比较危险了)
- 主从复制过程
- 第一次同步
- slave向master发送sync同步请求,master先dump出rdb文件,并将其全量传输给slave;master将产生rdb文件之后这段时间内的修改命令缓存起来,并发送给slave。首次同步完成。
- 第二次及以后的同步实现方式:
- master将变量的快照(有修改的变量)直接实时发送给slave。
- 如果发生断开重连,则重复第一步第二步
- reids-2.8版本之后,重连后进行第一步时,不用全量更新了。
- 第一次同步
- AOF方式
- AOF方式持久化过程
- Append Only File
- Redis将每次收到的命令都追加到文件中,类似于mysql的binlog;当redis重启时重新执行文件中的所有命令来重建数据
- 如果将所有命令不加甄别的都写入文件中,持久化文件会越来越大,比如INCR test命令执行100次,效果与SET test 100一样。此时需要进行rewrite,合并命令。
- Redis提供了bgrewriteaof命令,执行过程与产生RDB文件的机制类似,fork出的子进程将内存中的数据以命令的方式重写持久化文件。本质上讲,该命令是将数据库中所有数据内容以命令的方式重写进新的AOF文件
- AOF方式之我的想法
- AOF方式是redis在收到命名后将命令写入文件内,如果redis发生故障,重启时直接读取AOF文件重新执行命令即可恢复,可以克服RDB方式的缺点
- bgrewriteaof指令是对AOF方式的一次优化,执行bgrewriteaof命令时是根据此时数据库内容来写入AOF文件,并替换旧的AOF文件。这个过程与RDB快照产生方式一样
- RDB方式和AOF方式的对比
- RDB方式恢复起来快,而AOF方式需要一条条命令执行
- RDB文件不需要经过编码,是数据库内容的直接克隆,所以文件比较小;而AOF文件内是一条条命令,需要依次执行
- RDB文件可能会丢失部分数据,而AOF则专门解决这个问题
- 选择哪种方式
- 官方推荐:
- 如果想要很高的数据保障,则同时使用两种方式
- 如果可以接受数据丢失,则仅使用RDB方式
- 通常的设计思路是
- 利用replication机制弥补持久化在性能和设计上的不足
- master上不做RDB和AOF,保证读写性能
- slave同时开启两种方式,保证数据安全性
- 官方推荐:
- Redis数据恢复过程
- AOF优先级高于RDB方式,如果同时配置了AOF和RDB,AOF生效
void loadDataFromDisk(void) {
long long start = ustime();
if (server.aof_state == REDIS_AOF_ON) {
if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
} else {
if (rdbLoad(server.rdb_filename) == REDIS_OK) {
redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
(float)(ustime()-start)/1000000);
} else if (errno != ENOENT) {
redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
exit(1);
}
}
}
No comments:
Post a Comment