就算Redis键设置了过期时间,生产环境也不推荐统计键数量吗?
生产环境Redis模式匹配键统计问题解答
关于带过期策略是否还不推荐键统计的问题
首先给出明确结论:哪怕配置了自动过期删除策略,依然不推荐在生产环境直接执行模式匹配的全量键统计操作,原因如下:
- 大家常说的禁止在生产用的
KEYS命令本身是O(N)复杂度,需要遍历Redis实例的全量键,已过期但还未被惰性删除/定期删除策略清理的键依然会被纳入遍历范围,过期策略不会降低遍历的时间复杂度,高负载场景下会长时间阻塞Redis主线程,导致所有正常的读写请求超时。 - 就算你用的是集群版Redis,
KEYS命令只会扫当前节点的键,但只要节点键数量大,依然会有阻塞风险,和是否配置过期没有关系。
针对Laravel活跃用户统计场景的优化方案
你当前的实现逻辑是每个活跃用户对应一个带过期时间的Redis键,靠统计匹配模式的键数量算活跃用户数,针对这个场景推荐3种优化方案,你可以根据业务精度要求选:
- 方案1:用HyperLogLog做去重统计(最推荐,性能最高)
不需要给每个用户单独存键,按统计周期(日活/周活/月活)维护对应的HyperLogLog结构即可:- 用户活跃时调用
Redis::pfAdd('active_users:'.date('Ymd'), [$user->id])添加用户ID到对应周期的HyperLogLog中,同时给这个键设置对应周期的过期时间(比如日活的键设置过期时间2天即可) - 统计活跃用户数直接调用
Redis::pfCount('active_users:'.date('Ymd')),时间复杂度为O(1),单个HyperLogLog最多只占12KB内存,统计误差在0.81%以内,完全满足绝大多数活跃用户统计的业务要求。
- 用户活跃时调用
- 方案2:用有序集合(ZSet)实现灵活时间范围统计
如果需要支持自定义时间范围的活跃用户统计,可以用ZSet结构存储:- 用户活跃时调用
Redis::zAdd('active_users', time(), $user->id),把用户的最后活跃时间作为score写入ZSet - 统计时调用
Redis::zCount('active_users', $startTime, $endTime)即可拿到指定时间范围内的活跃用户数,时间复杂度为O(log(N)),远高于全量遍历键的性能,你也可以定期清理score早于过期阈值的成员,或者按周期拆分ZSet设置过期时间。
- 用户活跃时调用
- 方案3:用SCAN命令替代KEYS(兼容你现有逻辑)
如果业务原因必须保留每个用户单独存带过期键的逻辑,绝对不要用KEYS命令,改用SCAN命令分批迭代匹配对应模式的键:
Laravel中可以直接调用Redis::scan方法实现分批遍历,不会长时间阻塞Redis主线程,需要注意SCAN返回的结果会存在重复,需要自行做去重处理,同时可以搭配TTL命令过滤掉已经过期未清理的无效键。
内容的提问来源于stack exchange,提问作者JSTR
相关产品推荐
相关产品推荐

