如何在Django Redis缓存键超时删除时执行回调清理代码?
如何在Django Redis缓存键超时删除时执行回调清理代码?
嗨,这个需求我之前也碰到过!默认情况下Redis的键超时删除是后台被动/定期处理的,不会主动通知Django触发回调,所以得靠一些额外手段来实现。结合你用的django-redis==5.0.0,给你几个实用的方案:
方案一:利用Redis键空间通知(Keyspace Notifications)
这是最贴合“键超时自动触发”需求的方案,Redis支持通过订阅特定频道来监听键的过期事件。
步骤1:开启Redis的键空间通知
首先得修改Redis配置(或者动态设置),让它发送过期事件通知:
- 打开
redis.conf,找到notify-keyspace-events配置项,改成notify-keyspace-events "Ex"(E表示键事件,x表示过期事件) - 或者在Redis命令行执行动态配置(重启后失效):
redis-cli config set notify-keyspace-events Ex
步骤2:在Django中监听过期事件
你可以在Django里启动一个独立的线程(或者用守护进程)来订阅Redis的过期事件频道,处理特定键的清理逻辑。示例代码如下:
import redis from django.conf import settings def listen_for_expired_keys(): # 用django-redis的配置连接Redis r = redis.Redis.from_url(settings.CACHES['default']['LOCATION']) # 订阅0号数据库的过期事件频道(如果你的缓存用了其他数据库,替换成对应数字) pubsub = r.pubsub() pubsub.subscribe('__keyevent@0__:expired') for message in pubsub.listen(): # message['data']是字节类型,转成字符串 key = message['data'].decode('utf-8') # 判断是不是我们要处理的键 if key.startswith('clean_me_up_'): # 提取ID,执行你的清理代码 item_id = key.split('_')[-1] print(f"执行清理逻辑,ID:{item_id}") # 这里替换成你的实际清理操作,比如删除关联资源、更新数据库等 # cleanup_function(item_id) # 可以在Django启动时启动这个监听线程,比如在apps.py的ready方法里 # 注意:生产环境建议用独立进程或者守护进程运行,避免Django重启时中断监听
优缺点
- 优点:能精准监听Redis自动触发的键过期事件,和缓存过期逻辑强绑定
- 缺点:需要修改Redis配置,过期事件可能有轻微延迟(Redis的定期删除策略导致),监听线程/进程需要稳定运行,否则会错过事件
方案二:用Celery定时任务模拟回调
如果你的项目已经在用Celery,这个方案会更简单——在设置缓存的同时,创建一个和缓存超时时间一致的定时任务,到点自动执行清理逻辑。
示例代码
首先定义Celery任务:
# tasks.py from celery import shared_task from django.core.cache import cache @shared_task def cleanup_task(item_id): # 可选:先检查缓存键是否已经被删除(避免手动删除缓存后任务仍执行) if not cache.get(f'clean_me_up_{item_id}'): # 执行你的清理代码 print(f"执行清理逻辑,ID:{item_id}") # cleanup_function(item_id)
然后在设置缓存的地方同时调用定时任务:
from django.core.cache import cache from .tasks import cleanup_task # 设置缓存 cache.set(f'clean_me_up_{id}', 'some_value', timeout=10) # 同时创建定时任务,10秒后执行 cleanup_task.apply_async(args=[id], countdown=10)
优缺点
- 优点:不需要修改Redis配置,依赖Celery的话实现成本低,任务执行更可靠(Celery有重试机制)
- 缺点:如果缓存被手动提前删除,定时任务还是会执行(不过可以加缓存存在性检查避免);如果缓存超时时间被修改,任务的countdown也要同步调整
方案三:自定义缓存后端(仅适用于主动删除场景)
如果你还需要处理**主动调用cache.delete()**时的回调,可以自定义一个缓存后端继承django-redis的RedisCache,重写delete方法:
# custom_cache.py from django_redis.cache import RedisCache class CustomRedisCache(RedisCache): def delete(self, key, version=None, client=None): # 先执行父类的删除逻辑 result = super().delete(key, version, client) # 判断是不是目标键,执行清理代码 if key.startswith('clean_me_up_'): item_id = key.split('_')[-1] # cleanup_function(item_id) return result
然后在settings.py里配置使用这个自定义后端:
CACHES = { 'default': { 'BACKEND': 'yourapp.custom_cache.CustomRedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', # 其他django-redis配置... } }
⚠️ 注意:这个方案无法处理Redis自动超时删除的情况,只能处理主动调用delete()的场景,所以如果你的需求是监听自动超时,这个方案只能作为补充。
备注:内容来源于stack exchange,提问作者A. L
相关产品推荐
相关产品推荐

