设置定期清除时间:
EXPIRE key ttl
“定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作。
·惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
·定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。”
RDB过期处理:
主服务器会过滤过期key。从服务器不会。
AOF过期处理:
当key过期被清理的过程,在aof文件追加一个del操作。
AOF重写:
重写过程会忽略过期的key。
RDB:
SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求
如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态。
只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态。
int rdbSave(char *filename, rdbSaveInfo *rsi) {
char tmpfile[256];
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
FILE *fp = NULL;
rio rdb;
int error = 0;
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
if (!fp) {
char *cwdp = getcwd(cwd,MAXPATHLEN);
serverLog(LL_WARNING,
"Failed opening the RDB file %s (in server root dir %s) "
"for saving: %s",
filename,
cwdp ? cwdp : "unknown",
strerror(errno));
return C_ERR;
}
rioInitWithFile(&rdb,fp);
startSaving(RDBFLAGS_NONE);
if (server.rdb_save_incremental_fsync)
rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);
if (rdbSaveRio(&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) {
errno = error;
goto werr;
}
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp)) goto werr;
if (fsync(fileno(fp))) goto werr;
if (fclose(fp)) { fp = NULL; goto werr; }
fp = NULL;
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
if (rename(tmpfile,filename) == -1) {
char *cwdp = getcwd(cwd,MAXPATHLEN);
serverLog(LL_WARNING,
"Error moving temp DB file %s on the final "
"destination %s (in server root dir %s): %s",
tmpfile,
filename,
cwdp ? cwdp : "unknown",
strerror(errno));
unlink(tmpfile);
stopSaving(0);
return C_ERR;
}
serverLog(LL_NOTICE,"DB saved on disk");
server.dirty = 0;
server.lastsave = time(NULL);
server.lastbgsave_status = C_OK;
stopSaving(1);
return C_OK;
werr:
serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));
if (fp) fclose(fp);
unlink(tmpfile);
stopSaving(0);
return C_ERR;
}
BGSAVE:
BGSAVE执行时,SAVE、BGSAVE、BGREWRITEAOF不可执行。
记录秒数、备份次数、最后备份时间
struct saveparam {
time_t seconds;
int changes;
};
long long dirty; /* Changes to DB from the last save */
time_t lastsave; /* Unix time of last successful save */
程序会每100ms检测一遍savparam是否需要备份。
RDB文件数据结构:
[REDIS][db_version][database][EOF][check_sum]
REDIS: ‘R’‘E’‘D’‘I’'S’字符
db_version: RDB备份第几版本
database 数据键值对
EOF 1字节 标志数据读取完毕
check_sum: 前几项计算得出。用于校验。
AOF:
appendfsync:
no : 将aof_buf写入aof文件中,但不做同步操作,何时同步由操作系统决定。
everysec: 将aof_buf写入aof文件中,默认每秒同步一次。
always: 每次操作同步一次。
“Redis读取AOF文件并还原数据库状态的详细步骤如下:
1)创建一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
2)从AOF文件中分析并读取出一条写命令。
3)使用伪客户端执行被读出的写命令。
4)一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止。”
摘录来自: 黄健宏 著. “Redis设计与实现 (数据库技术丛书)。” Apple Books.
aof文件由于是增量,会占用很大磁盘与内存,redis通过实现aof文件重写功能使aof文件缩小。
按照现有的数据库进行add,每64item分成一个add进行操作,避免buf溢出。
aof重写后台操作会有一个数据在备份的过程中发生改变,这里redis会将操作记录在一个缓冲区,等aof重写后将缓冲区写入aof文件中即可。