概述
Redis为内存数据库,即所有的键值对信息保存在内存中,那么一旦服务器出现问题重启,内存中的数据就会没有了。所以Redis需要实现持久化,将内存中的数据持久化到硬盘,在重新启动后,又将硬盘中的数据加载到内存中。
RDB文件生成与载入
- 有两个命令可用于生成RDB文件,save和bgsave:
Save:save命令会阻断服务器进程,直到RDB文件生成,在阻塞期间,Redis服务器不会出现任何请求。
bgsave:bgsave命令会派生出一个子进程,然后由子进程来创建RDB文件,服务器进程继续出现请求命令。
- Redis中RDB文件载入没有专门的命令,只要在redis服务器重启的过程中,检测到rdb文件的存在就会自动加载。(这里先忽略AOF的情况,因为在AOF开启的情况下,服务器会优先加载AOF文件,因为AOF的更新频率通常比较RDB的高)
- 命令执行的服务器状态
- SAVE命令会阻塞服务器的请求,也就是保存期间,如果服务器有新的请求过来,不会去处理请求直到RDB文件生成;
- BGSAVE命令,会fork一个子进程出来,所以服务器能够处理其他请求命令;
- 如果在执行BGSAVE命令时,客户端再次执行SAVE,BGSAVE或BGREWRITEAOF呢?
- 不能同时执行,避免竞争条件发生。
- 在RDB文件载入时,所有请求均处于阻塞状态。
我们不可能每次手动去调用命令来持久化,Redis自身肯定是有一套策略的,什么时候进行持久化。
默认情况下,redis在下列条件满足其一,BGSAVE命令就会执行(可通过配置修改):
- 服务器在900秒内对数据库进行了至少一次修改;
- 服务器在300秒内对数据库进行了至少10次修改;
- 服务器在60秒内对数据库进行了至少10000次修改;
可以看一下数据结构:
在redisServer的数据结构中,有个属性用于记录保存条件的修改:
/* RDB persistence */ long long dirty; /* Changes to DB from the last save */ long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */ pid_t rdb_child_pid; /* PID of RDB saving child */ struct saveparam *saveparams; /* Save points array for RDB */
再看下saveparam的数据结构:
struct saveparam { time_t seconds; //时间 int changes; //修改数 };
此外,redisServer中还有两个属性dirty和lastsave:
/* RDB persistence */ long long dirty; /* Changes to DB from the last save,上次保存命令执行后,数据库的修改次数 */ long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */ pid_t rdb_child_pid; /* PID of RDB saving child */ struct saveparam *saveparams; /* Save points array for RDB */ int saveparamslen; /* Number of saving points */ char *rdb_filename; /* Name of RDB file */ int rdb_compression; /* Use compression in RDB? */ int rdb_checksum; /* Use RDB checksum? */ time_t lastsave; /* Unix time of last successful save ,上次成功保存的时间*/
RedisServer会定期执行serverCron(100ms一次),每次执行时,会检测之前默认的条件是否满足,如果满足条件则执行bgsave命令。程序会遍历saveparam中的数组,判断是否满足条件。
/* If there is not a background saving/rewrite in progress check if(之前会判断是否有AOF或BSAVE命令正在执行) * we have to save/rewrite now */ for (j = 0; j < server.saveparamslen; j++) { struct saveparam *sp = server.saveparams+j; /* Save if we reached the given amount of changes, * the given amount of seconds, and if the latest bgsave was * successful or if, in case of an error, at least * REDIS_BGSAVE_RETRY_DELAY seconds already elapsed. */ if (server.dirty >= sp->changes && server.unixtime-server.lastsave > sp->seconds && (server.unixtime-server.lastbgsave_try > REDIS_BGSAVE_RETRY_DELAY || server.lastbgsave_status == REDIS_OK)) { redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...", sp->changes, (int)sp->seconds); rdbSaveBackground(server.rdb_filename); break; } }
查看odb文件,如下,set 一个msgkey的内容为aaa,rdb文件如下:
redis 127.0.0.1:6379> keys *
(empty list or set)
redis 127.0.0.1:6379> set msg aaa
redis 127.0.0.1:6379> BGSAVE
Background saving started
[root@test bin]# od -c dump.rdb
0000000 R E D I S 0 0 0 6 376