前言
在使用缓存的时候, 我们时不时会遇到这样一个需求, 根据缓存键的规则去批量删除这些数据, 比较常见的就是按前缀去删除
举个简单的例子, Redis 中现在有几百个商品的数据, 这些数据的 key 值是有一定规律的, 都是以 product:id 的形式存在的
现在由于不得以为的原因要删除这几百个商品的数据, 这个时候我们肯定就要把缓存键以 product: 开头的给全部删除掉
其实这个需求在 Redis 中是可以很容易去实现的
来看看几种常见的做法
常见的几种做法
用 Keys 命令找到 key 之后执行删除操作
用 Scan 命令找到 key 之后执行删除操作 (2.8.0 版本之后)
添加缓存数据的时候, 可以同时将 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
来源: http://www.bubuko.com/infodetail-2506704.html