Redis里怎么搞个机制,记录每个Key最后用的时间,方便查查用没用过
- 问答
- 2026-01-01 11:37:36
- 2
在Redis里搞个机制记录每个Key最后被使用的时间,这个需求很常见,目的就是为了看看哪些数据是“冷”的,长时间没人碰,可能就可以清理掉了,腾出空间,Redis自己并没有直接提供一个命令叫LASTUSED来给你看这个时间,但是我们可以利用它现有的一些特性和命令,自己动手搭出这个机制,下面我就直接说几种方法,你看哪种适合你的情况。
利用Redis的OBJECT IDLETIME命令
这是最省事的方法,因为你什么都不用做,Redis从2.2.3版本开始,内部其实已经悄悄地在为每个Key记录它空闲了多久(也就是自上次被访问到现在过了多少秒),这个命令就是OBJECT IDLETIME key,你只需要对着你想查的Key,敲这个命令,它就会返回一个数字,单位是秒。
你有个Key叫user:1000:profile,你想知道它多久没被读写了,就执行:
OBJECT IDLETIME user:1000:profile
它会返回像(integer) 3600这样的结果,意思是这个Key已经3600秒(1小时)没人动过了。
这个方法有几个重要的限制,你得心里有数:
- 它只记录“读”或“写”操作:像
DEL这种删除操作也会重置空闲时间,但有些操作不算,比如你光用EXISTS检查Key存不存在,是不会重置这个空闲时间的,如果一个Key只是因为被检查是否存在而变得“活跃”,在这个命令看来它还是空闲的。 - 这个值在Redis主从复制或者AOF重写时会被重置:这是个比较坑的地方,当发生主从同步或者AOF文件重写时,Key会被认为是“被访问”了,空闲时间会归零,所以这个值在持久化场景下可能不准。
- 你只能查,不能用来做自动化过期:
OBJECT IDLETIME只是个查询命令,它返回的值不能被Redis的过期策略(比如volatile-lru)直接使用,也就是说,你不能设置一个规则说“当Key空闲超过7天就自动删除”,因为这个空闲时间信息没有和过期机制挂钩。
如果你只是偶尔手动查一下,看看哪些Key可能可以删了,这个方法最简单快捷,但如果你想基于这个“最后使用时间”来做自动化的清理,或者需要非常精确的记录,那这个方法就不行了。
自己用额外的Key来记录时间戳
既然内置的不够用,我们就自己造,思路很简单:每次你访问(读或写)一个主Key的时候,都顺手在一个专门的地方记录下当前的时间戳。
具体怎么做呢?有两种常见的子方案:
子方案A:给每个主Key配一个“影子Key”
比如你有一个主Key叫 data:important,你同时创建一个对应的Key,叫 last_used:data:important,每次你访问 data:important 之前或之后,都执行一个命令去设置这个影子Key的值为当前时间戳。
- 写操作时:你写主Key,同时也要设置影子Key,比如用
SET命令,可以这样:MULTI SET data:important "你的数据" SET last_used:data:important 1657789200 EXEC用
MULTI/EXEC把两个命令包成一个事务,保证它们一起执行。 - 读操作时:你读主Key,同时也要更新影子Key,比如用
GET命令,可以这样:GET data:important SET last_used:data:important 1657789200这里两个命令是分开的,严格来说不是原子性的,如果你非常要求一致性,可以考虑用Lua脚本把这两个操作绑在一起原子性地执行。
这样,你想知道data:important最后什么时候被用过,直接GET last_used:data:important就行了,拿到的是一个Unix时间戳。
子方案B:用一个有序集合(Sorted Set)来统一管理 如果你有超级多的Key,给每个Key都配一个影子Key,会让Key的数量翻倍,管理起来可能有点乱,这时可以用一个有序集合来记录所有Key的最后使用时间。
这个有序集合的member就是每个主Key的名字,score就是对应的时间戳。
- 每次访问任何一个主Key时,都执行一个命令:
ZADD last_used_timestamps 1657789200 data:important这个命令的意思是,向名为
last_used_timestamps的有序集合里,添加一个成员data:important,并给它的分数(score)设置为当前时间戳1657789200,如果这个成员已经存在,ZADD命令会更新它的分数为新的时间戳,这正是我们想要的。
这个方法的优点是:
- 所有信息集中管理:你只有一个Key(那个有序集合)来存储所有元数据,比较清爽。
- 方便批量查询和清理:因为有序集合是按分数(也就是时间戳)排序的,你可以非常方便地找出最久没被使用的Key,用
ZRANGE last_used_timestamps 0 -1 WITHSCORES可以列出所有Key和它们的时间戳,或者用ZREVRANGE按时间倒序排,想找N天前活跃的Key,可以用ZRANGEBYSCORE last_used_timestamps 0 1657184400(假设1657184400是7天前的时间戳)。
利用Redis的发布订阅(Pub/Sub)功能解耦
上面第二种方法要求你在所有访问Redis的代码里,都记得加上那条更新最后使用时间的命令,如果你的系统很复杂,或者你不想动那么多业务代码,可以用发布订阅模式来解耦。
基本思路是:你让Redis在每次有命令执行时,自动发布一个通知,然后你写一个单独的订阅者程序(比如一个脚本或者一个小服务),专门来接收这些通知,并更新最后使用时间。
Redis默认不会发布这样的通知,你需要先配置它,在Redis的配置文件redis.conf里,有一项叫notify-keyspace-events,你需要把它设置成AKE(或者至少包含A和K的含义)。A表示所有事件,K表示键空间事件,这样配置后,Redis会在Key被访问(读、写、删等)时,向一个特定的频道发送消息。
你启动一个订阅客户端,订阅__keyspace@0__:*这样的频道(0是数据库编号),当有事件发生时,比如有人GET了data:important,你的订阅客户端就会收到一条消息,告诉你data:important发生了get事件。
你的订阅客户端收到消息后,就可以根据事件类型(比如忽略exists事件,只关心get, set等),去更新你那个记录最后使用时间的结构(比如上面方法二里的影子Key或有序集合)。
这个方法的优点是:
- 业务代码无侵入:你不需要修改任何读写Redis的业务逻辑,所有记录时间的活儿都由后台的订阅者干了。
- 集中处理:逻辑都在一个地方,好管理。
缺点是:
- 配置复杂点:需要改Redis配置并确保订阅者程序一直运行。
- 可靠性:Pub/Sub模式的消息是“即发即忘”的,如果订阅者当时挂了,消息就丢了,最后使用时间也就没记上,如果你要求绝对不能丢,可能需要更复杂的机制。
- 图省事,偶尔手动查查:直接用
OBJECT IDLETIME命令。 - 需要自动化,能接受改业务代码:用方法二,自己更新影子Key或有序集合,其中有序集合方式更利于批量分析。
- 系统复杂,不想动业务代码:考虑用方法三的发布订阅模式,但要注意消息可能丢失的风险。
无论用哪种方法,特别是自己记录时间戳的,都要注意一下性能开销,比如方法二,每次访问多写一个Key;方法三,虽然解耦了,但Redis服务器本身需要多发出消息,如果你的QPS非常高,需要评估一下这些额外操作带来的影响。

本文由召安青于2026-01-01发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/72428.html
