redis key 的过期机制

今天有人问了我一个问题:redis 的 key 过期是怎么实现的。我用redis做了几个功能,竟然没有思考这个问题,该死。作为一个敏而好学的好童鞋,立马google和查文档。

redis key 的过期机制


翻了下文档:

Redis keys are expired in two ways: a passive way, and an active way.

redis keys 过期有两种方式: passive 和 active.

active


active, 主动的方式:

当某个客户端尝试访问这个key时, 若这个key被发现timeout了, 那么就删除, 否则正常访问.

这篇博文 摘出了源码, 如下:

int expireIfNeeded(redisDb *db, robj *key) {
mstime_t when = getExpire(db,key);
mstime_t now;

if (when < 0) return 0; /* No expire for this key */

/* Don't expire anything while loading. It will be done later. */
if (server.loading) return 0;

/* If we are in the context of a Lua script, we claim that time is
* blocked to when the Lua script started. This way a key can expire
* only the first time it is accessed and not in the middle of the
* script execution, making propagation to slaves / AOF consistent.
* See issue #1525 on Github for more information. */
now = server.lua_caller ? server.lua_time_start : mstime();

/* If we are running in the context of a slave, return ASAP:
* the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys.
*
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
/*如果我们正在slaves上执行读写命令,就直接返回,
*因为slaves上的过期是由master来发送删除命令同步给slaves删除的,
*slaves不会自主删除*/
if (server.masterhost != NULL) return now > when;
/*只是回了一个判断键是否过期的值,0表示没有过期,1表示过期
*但是并没有做其他与键值过期相关的操作*/

/* Return when this key has not expired */
/*如果没有过期,就返回当前键*/
if (now <= when) return 0;

/* Delete the key */
/*增加过期键个数*/
server.stat_expiredkeys++;
/*传播键过期的消息*/
propagateExpire(db,key);
notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,"expired",key,db->id);
/*删除过期键*/
return dbDelete(db,key);
}

passive


passive, 被动的方式:

redis 每秒会执行10次以下操作 (相关源码在server.cdatabasesCron()中) :

  1. 测试20个随机keys.
  2. 删除找到的过期keys.
  3. 如果清除的keys超过25%, 那么认为当前数据库的过期keys是比较多的, 会继续抽样清除操作, 从第一步来过.

passive 和 active 方式是同时工作.

最后


虽然没看文档的这部分, 但其实一开始我直觉是: 莫非设置了定时器 ? 但想想也不合适,这样消耗太大了。出于性能的考虑,隐然觉得redis应该会有定时清理的功能,而且联想到之前的做的一个支付订单的功能和这个机制有点类似:首先有一个定时扫描来禁用过期的支付订单,当想继续支付前(使用该支付订单),需要检查这个支付订单有没有过期,没有过期才让继续支付。

这个问题,我竟懵逼了。如果问题换成让我设计这个机制, 或许能回答出来。 %>_<%

参考


EXPIRE – Redis
源码
How does redis expire keys? - Stack Overflow
http://blog.dolphin-game.com/post/65.html