Redis如何批量失效key
问题
Redis的del key
命令只能删除指定key。如果要删除满足指定模式的key,则会比较麻烦。这里提供一个使用数据时间戳的方案来实现批量失效缓存
场景描述
最近在写一个好友关系模块,类似于微信的好友机制。其中有一个方法Friendship getFriendship(long userId, long targerId);是用于获取两个用户是否有权限聊天
/*只要有一个字段为true,则可以聊天*/
class Friendship{
boolean isFriend; //用户是否添加对方为好友,a添加b为好友,但是b未必添加a为好友,场景与微信类型。
boolean isWhite; //如果有一方是白名单用户可以直接聊天
}
复制代码
由于该方法调用比较频繁,所以决定对该数据进行缓存。既然使用缓存就要清楚什么场景下需要添加缓存,什么场景下需要失效缓存
添加缓存
什么时候添加缓存是比较明显,当一个用户查询与另外一个用户的好友关系时加入缓存
Friendship getFriendship(long userId, long targerId){
String key = String.format("Friendship:%s_%s", userId, targerId);
Friendship friendship = redisServer.getObject(key, Friendship.class);
if(friendship == null){
friendship = queryFriendship(userId, targerId);
redisServer.setObject(key, friendship);
}
return friendship;
}
复制代码
失效缓存
失效缓存分为两部分
- isFriendship 这部分是比较简单的,只要userId方删除了targerId方,则删除为
Friendship:userId_targertId
的key即可 - isWhite 这部分稍微比较麻烦一点。因为这个当一个用户被添加/删除白名单时,那么所有包含该用户的key都需要删除。虽然redis提供了
keys pattern
的命令用于检索关键字,但这个命令并不适合在生产上使用。比如添加/删除了一个id为123的白名单用户,那么所有符合正则Friendship:123_\d+
和Friendship:\d+_123
的键就都要失效
解决方案
其实失效缓存并不一定要删除redis数据,只要提供一个时间戳字段用于判断缓存的有效性即可。
由于白名单用户变动比较少,为了实现上的简单,这里采用失效所有好友关系缓存的方式。即删除所有符合正则Friendship:\d+_\d+
的key。问题本质与删除指定用户的key一样
白名单用户变更
void updateWhitelist(long userId, int operate){
//update whitelist
//保存最近一次变更白名单数据的时间
String key = "UpdateWhitelist";
long timestamp = System.currentTimeMillis();
redisService.setLong(key, tiemstamp);
}
复制代码
Friendship类
class Friendship{
boolean isFriend;
boolean isWhite;
long timestamp; //缓存数据时的时间戳
}
复制代码
getFriendship 方法
Friendship getFriendship(long userId, long targerId){
//如果没有更新,默认返回0
long updateWhitelistTimestamp = redisServer.getLong("UpdateWhitelist", 0);
String key = String.format("Friendship:%s_%s", userId, targerId);
Friendship friendship = redisServer.getObject(key, Friendship.class);
//好友关系缓存不存在或者缓存时间小于白名单更新时间,则重新查询数据库
if(friendship == null
|| friendship.getTimestamp() <= updateWhitelistTimestamp){
friendship = queryFriendship(userId, targerId);
friendship.setTimestamp(System.currentTimeMillis())
redisServer.setObject(key, friendship);
}
return friendship;
}
复制代码
这样当Redis缓存了两个用户好友关系数据后,有新的用户被添加进了报名单。后续的查询根据timestamp的比较就会让当前缓存失效从而获取新的数据