共计 3614 个字符,预计需要花费 10 分钟才能阅读完成。
这篇文章将为大家详细讲解有关 redis 之 RDB、AOF 与复制时对过期键怎么处理,丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
生成 RDB 文件
在执行 SAVE 命令或者 BGSAVE 命令创建一个新的 RDB 文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的 RDB 文件中。
举个例子,如果数据库中包含三个键 k1、k2、k3,并且 k2 已经过期,那么当执行 SAVE 命令或者 BGSAVE 命令时,程序只会将 k1 和 k3 的数据保存到 RDB 文件中,而 k2 则会被忽略。
因此,数据库中包含过期键不会对生成新的 RDB 文件造成影响。
可参考 rdb.c 中函数 rdbSave() 函数源码:
/* Iterate this DB writing every entry
*
* 遍历数据库,并写入每个键值对的数据
*/
while((de = dictNext(di)) != NULL) { sds keystr = dictGetKey(de);
robj key, *o = dictGetVal(de);
long long expire;
// 根据 keystr ,在栈中创建一个 key 对象
initStaticStringObject(key,keystr);
// 获取键的过期时间
expire = getExpire(db, key);
// 保存键值对数据
if (rdbSaveKeyValuePair( rdb, key,o,expire,now) == -1) goto werr;
}
rdbSaveKeyValuePair 函数实现如下:
/* Save a key-value pair, with expire time, type, key, value.
*
* 将键值对的键、值、过期时间和类型写入到 RDB 中。 *
* On error -1 is returned.
*
* 出错返回 -1 。 *
* On success if the key was actually saved 1 is returned, otherwise 0
* is returned (the key was already expired).
*
* 成功保存返回 1 ,当键已经过期时,返回 0 。 */
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
long long expiretime, long long now)
/* Save the expire time
*
* 保存键的过期时间
*/
if (expiretime != -1) {
/* If this key is already expired skip it
*
* 不写入已经过期的键
*/
if (expiretime now) return 0;
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;
if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;
}
/* Save type, key, value
*
* 保存类型,键,值
*/
if (rdbSaveObjectType(rdb,val) == -1) return -1;
if (rdbSaveStringObject(rdb,key) == -1) return -1;
if (rdbSaveObject(rdb,val) == -1) return -1;
return 1;
}
载入 RDB 文件
在启动 Redis 服务器时,如果服务器开启了 RDB 功能,那么服务器将对 RDB 文件进行载入:
如果服务器以主服务器模式运行,那么在载入 RDB 文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载入 RDB 文件的主服务器不会造成影响;
如果服务器以从服务器模式运行,那么在载入 RDB 文件时,文件中保存的所有键,不论是否过期,都会被载入到数据库中。不过,因为主从服务器在进行数据同步的时候,从服务器的数据库就会被清空,所以一般来讲,过期键对载入 RDB 文件的从服务器也不会造成影响;
这部分代码可以查看 rdb.c 中 rdbLoad() 函数源码:
/* Check if the key already expired. This function is used when loading
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave.
*
* 如果服务器为主节点的话, * 那么在键已经过期的时候,不再将它们关联到数据库中去
*/
if (server.masterhost == NULL expiretime != -1 expiretime now) { decrRefCount(key);
decrRefCount(val);
// 跳过
continue;
}
AOF 文件写入
当服务器以 AOF 持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么 AOF 文件不会因为这个过期键而产生任何影响。
当过期键被惰性删除或者定期删除之后,程序会向 AOF 文件追加(append)一条 DEL 命令,来显式地记录该键已被删除。
举个例子,如果客户端使用 GET message 命令,试图访问过期的 message 键,那么服务器将执行以下三个动作:
1)从数据库中删除 message 键。
2)追加一条 DEL message 命令到 AOF 文件。(根据 AOF 文件增加的特点,AOF 只有在客户端进行请求的时候才会有这个 DEL 操作)
3)向执行 GET 命令的客户端返回空回复。
这部分就是 Redis 中的惰性删除策略中 expireIfNeeded 函数的使用。关于惰性删除策略这一部分在 Redis 惰性删除策略一篇中有讲。所以这里就不赘述了。
需要提示一下的是:expireIfNeeded 函数是在 db.c/lookupKeyRead() 函数中被调用,lookupKeyRead 函数用于在执行读取操作时取出键 key 在数据库 db 中的值。
AOF 重写
和生成 RDB 文件时类似,在执行 AOF 重写的过程中,程序会对数据库中的键进行检查,已过期的键不会被保存到重写后的 AOF 文件中。
举个例子,如果数据库中包含三个键 k1、k2、k3,并且 k2 已经过期,那么在进行重写工作时,程序只会对 k1 和 k3 进行重写,而 k2 则会被忽略。
这一部分如果掌握了 AOF 重写的方法的话,那就自然理解了。
复制
当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制:
主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个 DEL 命令,告知从服务器删除这个过期键;
从服务器在执行客户端发送的读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键;
从服务器只有在接到主服务器发来的 DEL 命令之后,才会删除过期键。
举个例子,有一对主从服务器,它们的数据库中都保存着同样的三个键 message、xxx 和 yyy,其中 message 为过期键,如图所示
如果这时有客户端向从服务器发送命令 GET message,那么从服务器将发现 message 键已经过期,但从服务器并不会删除 message 键,而是继续将 message 键的值返回给客户端,就好像 message 键并没有过期一样。
假设在此之后,有客户端向主服务器发送命令 GET message,那么主服务器将发现键 message 已经过期:主服务器会删除 message 键,向客户端返回空回复,并向从服务器发送 DEL message 命令,如图所示:
从服务器在接收到主服务器发来的 DEL message 命令之后,也会从数据库中删除 message 键,在这之后,主从服务器都不再保存过期键 message 了,如图所示:
关于“redis 之 RDB、AOF 与复制时对过期键怎么处理”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。