Redis如何批量失效key

2,054 阅读3分钟

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