记一次生产环境上从redis亿级缓存中批量删除key

    技术2022-07-11  85

    记一次生产环境上从redis亿级缓存中批量删除key

    一、添加maven依赖二、网上常见的scan模糊匹配批量删除key(游标不移动,错误方法)三、scan全局扫描模糊匹配批量删除key(游标在移动,正确方法)

    场景:由于之前第一个版本快速迭代,同事写redis的时候,没有设置超时时间,导致redis上线两个月之后,发现内存在一直在涨,故需要清除redis中没有设置超时时间的key

    方案:大家都知道生产环境不能使用keys命令来遍历,这可能会造成redis阻塞,cpu暴涨100%等情况产生,特别是在集群环境中存储量特别大的时候,上面所述现象尤为常见;一般在亿级缓存数据批量删除时,使用redis scan批量删除key尤为有效

    一、添加maven依赖

    <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>

    二、网上常见的scan模糊匹配批量删除key(游标不移动,错误方法)

    这种方法有个问题,那就是没法移动游标,无法全局扫描redis集群中所有匹配的key

    public void scan(String pattern, Consumer<byte[]> consumer,Long count) { this.stringRedisTemplate.execute((RedisConnection connection) -> { try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(pattern).count(count).build())) { cursor.forEachRemaining(item->{ String key = new String(item,StandardCharsets.UTF_8); log.info("clear key={}",key); connection.del(item); }); return null; } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } }); }

    三、scan全局扫描模糊匹配批量删除key(游标在移动,正确方法)

    这种方法才是正确的全局扫描批量删除redis key方式

    public Set<Object> scan(String pattern) { redisTemplate.execute((RedisCallback<Set<Object>>) connection -> { JedisCommands commands = (JedisCommands) connection.getNativeConnection(); MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands; int scanInvokeCount = 0; int totalCount = 0; log.info("RedisHelper_clearScan_invoke_start scanInvokeCount:{}",scanInvokeCount); ScanParams scanParams = new ScanParams(); scanParams.match(pattern + "*"); scanParams.count(500); ScanResult<String> scan = multiKeyCommands.scan("0", scanParams); scanInvokeCount++; while (null != scan.getStringCursor()) { List<String> keys = scan.getResult(); if (!CollectionUtils.isEmpty(keys)){ int count = 0; for (String key : keys) { try { connection.del(key.getBytes()); log.info("RedisHelper_clearScan key:{}",key); count++; totalCount++; } catch (Exception e) { log.info("RedisHelper_clearScan_fail key:{}",key); e.printStackTrace(); } } log.info("RedisHelper_clearScan_delete count:{}",count); } if (!StringUtils.equals("0", scan.getStringCursor())) { scan = multiKeyCommands.scan(scan.getStringCursor(), scanParams); scanInvokeCount++; log.info("RedisHelper_clearScan_invoke scanInvokeCount:{}",scanInvokeCount); continue; } else { break; } } log.info("RedisHelper_clearScan_invoke_end totalCount:{}",totalCount); log.info("RedisHelper_clearScan_invoke_end scanInvokeCount:{}",scanInvokeCount); return null; }); return null; }
    Processed: 0.013, SQL: 9