使用 keys() 在生产环境中会导致性能下降、阻塞其他请求、数据不一致、内存消耗过大和安全风险,因此在线上环境应避免使用,下面给出一种解决查询系统当前用户不适用keys()的方案
使用 Redis Set 数据结构实现
- 将每个在线用户的唯一标识(如用户ID)存储在一个 Redis Set 中。
- 当用户上线时,使用 SADD 将用户ID添加到 Set 中。
- 当用户下线时,使用 SREM 将用户ID从 Set 中移除。
- 为每个在线用户的唯一标识(如用户ID)设置一个TTL(过期时间)
- 定期清理过期的用户。
- 统计在线用户时,使用 key 命令获取 Set 中的元素,然后通过循环获取当前登录用户
#user1上线并设置TTL
SET user:user1:status online EX <ttl>
ZADD online_users_timestamp <timestamp> user1
#user2上线并设置TTL
SET user:user2:status online EX <ttl>
ZADD online_users_timestamp <timestamp> user2
代码实现
//登录时
redisTemplate.opsForSet().add(ALL_TOKENS_CACHE_KEY, value) //,存入key 进入set 集合
//查询时
Set<String> keys = redisTemplate.opsForSet().members(ALL_TOKENS_CACHE_KEY) // 读取set 集合中的所有key
for (String key : keys)
{
LoginUser user = redisCache.getCacheObject(key); //循环读取登录用户
//解决keys(*)时存入的Set<String> keys 中的key后期不知道是否过期的问题
if (user == null) {
redisCache.delCacheSetValue(ALL_TOKENS_CACHE_KEY, key);
continue;
}
}
定时任务去除set中过期token
@Component
public class RemoveExpireToken {
@Autowired
private SessionRedisCache sessionRedisCache;
public void remove() {
Set<String> keys = sessionRedisCache.getCacheSet(ALL_TOKENS_CACHE_KEY);
for (String key : keys) {
LoginUser user = sessionRedisCache.getCacheObject(key);
if (user == null) {
sessionRedisCache.delCacheSetValue(ALL_TOKENS_CACHE_KEY, key);
}
}
}
}