• 谈谈使用Redis缓存时批量删除的几种实现


    前言

    在使用缓存的时候,我们时不时会遇到这样一个需求,根据缓存键的规则去批量删除这些数据,比较常见的就是按前缀去删除。

    举个简单的例子,Redis中现在有几百个商品的数据,这些数据的key值是有一定规律的,都是以product:id的形式存在的。

    现在由于不得以为的原因要删除这几百个商品的数据,这个时候我们肯定就要把缓存键以product:开头的给全部删除掉。

    其实这个需求在Redis中是可以很容易去实现的。

    来看看几种常见的做法。

    常见的几种做法

    1. 用Keys命令找到key之后执行删除操作
    2. 用Scan命令找到key之后执行删除操作(2.8.0版本之后)
    3. 添加缓存数据的时候,可以同时将key存放到一个SET中,然后依据这个SET来执行删除操作

    对于Keys命令,网上有不少血的教训,对于生产环境还是要谨慎谨慎再谨慎!能不用就别用。

    Scan命令的话是大部分人推荐的做法,是增量式迭代的一个命令。

    存到SET中就相对繁琐一点,而且额外占用了一部分内存。而且在进行删除的时候还要从这里读取出相应的key,同时也要移除这部分key的数据。

    下面来看看如何在.NET Core中来处理,主要还是针对SCAN的做法。

    示例操作Redis用的是StackExchange.Redis

    使用IServer.Keys

    可能有人会有疑惑,不是说Keys命令尽量不要用吗?怎么你还用?

    这个还真的要解释一下!

    可能从方法上,我们找遍所有IServer和IDataBase接口都找不到纯粹的SCAN命令(SetScan,HashScan等除外)。

    但是如果看过里面的实现,你就会知道是为什么了!

    传送门:Keys

    可以看看下图高亮的两行代码:

    大致意思就是,如果你用的Redis的版本支持SCAN命令,走的就是SCAN,反之只能是KEYS了。

    下面定义一个查找RedisKey的方法。

    private static RedisKey[] SearchRedisKeys(IServer server, string pattern)
    {
        var keys = server.Keys(pattern: pattern).ToArray();
        Console.WriteLine("Search Count-{0}",keys.Length);
        return keys;
    }
    

    知道那些Key要删除,剩下的就比较简单了!

    private static void KeysOrScanSolution(IServer server,IDatabase db, string pattern)
    {            
        db.KeyDelete(SearchRedisKeys(server, pattern));                        
    }
    

    使用IDatabase.Execute

    IServer.Keys可以说是隐式的调用了SCAN命令,那么我们自然也可以显式的去调用这个命令来完成这些。

    private static RedisKey[] SearchRedisKeys(IDatabase db,string pattern)
    {
        var keys = new HashSet<RedisKey>();
    
        int nextCursor = 0;
        do
        {
            RedisResult redisResult = db.Execute("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000");
            var innerResult = (RedisResult[])redisResult;
    
            nextCursor = int.Parse((string)innerResult[0]);
    
            List<RedisKey> resultLines = ((RedisKey[])innerResult[1]).ToList();
    
            keys.UnionWith(resultLines);
        }
        while (nextCursor != 0);
    
        return keys.ToArray();
    }
    

    删除的代码。

    private static void ExecuteSolution(IDatabase db, string pattern)
    {
        db.KeyDelete(SearchRedisKeys(db, pattern));
    }
    

    当然还有一种做法是调用lua脚本去完成,这里就不细说了。

    总结

    虽然上面几种做法能比较简单的处理这个问题,但是在拿出这些Keys的时候,客户端的内存占用可能会比较大,尤其是有大量符合条件的缓存项的时候。

    涉及缓存的诸多操作(包含根据前缀去删除缓存项),我也在EasyCaching中实现了相应的操作,后面也会不断的抽时间来完善这一项目,有兴趣的朋友可以关注一下。

    文中的示例代码 RedisBatchRemoveSolution

  • 相关阅读:
    【软件工程】个人项目作业
    【软件工程】个人博客作业
    【软件工程】第一次作业-热身!
    OO第四单元总结
    OO第三单元总结
    OO第二单元总结
    OO第一单元总结
    提问回顾与个人总结
    [技术博客] Django中文件的保存与访问
    OO第三单元博客作业
  • 原文地址:https://www.cnblogs.com/catcher1994/p/8469366.html
Copyright © 2020-2023  润新知