当前位置:首页 > 问答 > 正文

Redis自动清理旧数据,怎么设置让它每天零点过期消失

关于Redis如何实现数据自动过期,特别是设定在每天零点这个特定时间点失效,其核心机制并非直接设置一个“每日零点”的过期指令,而是通过Redis内置的“过期时间”功能结合灵活的策略来实现,这个信息主要来源于Redis官方文档中关于“过期”(Expiration)的章节,下面将详细解释几种不同的方法。

核心概念:使用EXPIREAT命令设置绝对时间戳

Redis本身没有提供一个名为“EXPIREATMIDNIGHT”这样的命令,最接近需求的方法是使用EXPIREAT命令,这个命令允许你为一个键(key)设置一个绝对的Unix时间戳作为过期时间,当Redis服务器的系统时间到达或超过这个时间戳时,该键及其对应的值就会被自动删除。

Unix时间戳是指从1970年1月1日零点(UTC时间)开始所经过的秒数,要实现“每天零点过期”,我们需要计算出下一个零点时刻对应的Unix时间戳,然后将这个时间戳设置给特定的键。

如果你想让你存储在Redis中的一个名为daily_report:20231027的键在2023年10月28日零点过期,你需要先计算出2023年10月28日00:00:00的Unix时间戳(假设是1698451200),然后执行命令:EXPIREAT daily_report:20231027 1698451200,这样,一到那个时间点,Redis就会自动清理掉这个键。

在应用程序中计算并设置

这是最常用和直接的方法,具体操作在你的应用程序代码中完成。

  1. 存储数据:当你将数据存入Redis时,像平常一样使用SET等命令。

  2. 计算下一个零点的时间戳:在你的程序代码中(无论是Python、Java、Node.js等),使用日期时间库来计算当天之后的下一个零点的时间戳,在Python中,你可以使用datetime模块:

    import datetime
    import time
    # 获取当前时间
    now = datetime.datetime.now()
    # 计算第二天的零点时间
    next_midnight = (now + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
    # 转换为Unix时间戳
    timestamp = int(time.mktime(next_midnight.timetuple()))
  3. 设置过期时间:在将数据存入Redis后,立即使用计算出的时间戳执行EXPIREAT命令。

    # 假设r是你的Redis连接对象
    r.set('daily_report:20231027', '你的数据内容')
    r.expireat('daily_report:20231027', timestamp)

    注意:Redis也提供了一个组合命令SETEX,但它只能设置以秒为单位的相对过期时间,无法直接满足绝对时间点的需求,所以这里分开操作更合适。

这种方法的优点是灵活、直观,你可以轻松地为不同的数据键设置不同的过期策略,缺点是需要在每个写入数据的地方都添加这段逻辑。

使用Redis的键空间通知(Keyspace Notifications)配合脚本(进阶方法)

这个方法相对复杂,不适合初学者,但可以应对更特殊的场景,思路是利用Redis的另一个功能——键空间通知,你可以配置Redis,当有键过期或被删除时,它会发布一个消息到特定的频道。

  1. 配置Redis开启键空间通知:在Redis配置文件(redis.conf)中设置notify-keyspace-events Ex,然后重启Redis服务,这个配置表示启用对过期事件(Expired events)的通知。
  2. 设置一个长期存在的“触发器”键:创建一个键,比如叫midnight_trigger,并为它设置一个很短的过期时间(比如24小时,即86400秒),但这并不能精确到零点。
  3. 订阅过期消息并执行清理:编写一个独立的守护进程(一个一直在运行的程序),让它订阅Redis的过期事件频道,当它收到midnight_trigger键过期的消息时,就认为“新的一天开始了”,然后这个守护进程可以主动执行一个Lua脚本或一系列命令,去扫描和删除所有需要在那天被清理的旧数据,删除完成后,它再重新设置midnight_trigger键,并使其在24小时后再次过期,从而形成循环。

这个方法非常强大,可以将所有清理逻辑集中在一处管理,尤其适用于需要一次性清理大量符合某种模式的键(所有以temp:开头的键)的场景,它的缺点也非常明显:设置复杂,需要维护额外的守护进程,并且依赖于事件通知的可靠性,不适合简单的“每个键在设置后第二天零点过期”的需求,官方文档在“Keyspace Notifications”部分对此有详细说明。

结合使用TTL和定时任务(Cron Job)

这是一种间接的、基于外部调度的“兜底”方案,通常不作为主要方法,而是对上述方法的补充。

  1. 为数据设置一个较长的存活时间(TTL):为你存入的数据设置36小时(129600秒)的过期时间,使用EXPIRE命令即可,这意味着数据最多会在Redis中留存一天半。
  2. 设置系统定时任务(Cron Job):在运行Redis的服务器上,配置一个每天零点准时执行的定时任务(Cron Job),这个任务的内容是运行一个Redis命令脚本。
  3. 定时任务执行强制清理:这个脚本可以使用Redis的SCAN命令(推荐用于生产环境,避免阻塞)或KEYS命令(数据量小且不担心阻塞时可用)来查找所有已经超过24小时但还没被自动删除的旧数据(比如查找所有键名中包含前一天日期的键),然后使用DEL命令将它们删除。

这种方法的优点是不依赖于Redis的过期精度,能够进行强制清理,确保数据被删除,缺点是需要管理服务器上的定时任务,而且清理操作如果数据量很大可能会在瞬间对Redis性能产生一定影响(使用SCAN可以缓解),数据实际存活时间会比你预期的24小时要长一些(最多到36小时)。

总结与建议

对于“让数据每天零点过期”这个需求,最推荐、最实践的方法是第一种:在应用程序中计算下一个零点的时间戳,然后使用EXPIREAT命令

这种方法:

  • 精准:可以确保数据在指定的绝对时间点被删除。
  • 简单:逻辑清晰,易于理解和实现。
  • 可靠:完全利用Redis内置的、经过充分测试的过期机制,无需引入额外的复杂组件。
  • 低开销:Redis自己会在后台以高效的方式检查并删除过期键,对性能影响极小。

你不需要等待零点才存入数据,在任何时间点存入数据,都可以通过计算“下一个零点”的时间戳来统一过期时间,在10月27日下午3点存的数据,可以设置它在10月28日零点过期;而在10月27日晚上11点59分存的数据,同样也是设置它在10月28日零点过期。

Redis自动清理旧数据,怎么设置让它每天零点过期消失