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的比较就会让当前缓存失效从而获取新的数据