redis之RDB、AOF与复制时对过期键怎么处理

127次阅读
没有评论

共计 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 与复制时对过期键怎么处理”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-28发表,共计3614字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)