在 Spring Boot 应用中使用 Spring Cache 管理缓存时,可以通过调用 @CacheEvict(allEntries=true)
注解的方法来批量删除当前缓存(cacheNames
) 下的所有缓存项目。如下:
package cn.springdoc.demo.cache;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Component;
@Component
public class FooCahe {
// 清除 "foo" 命名空间下的所有缓存项目
@CacheEvict(cacheNames = "foo", allEntries = true)
public void clear () {
}
}
如果你的 Spring Cache 使用的缓存实现是 Redis,那么默认情况下它会使用 KEYS [pattern]
指令来获取、删除所有匹配的缓存项目。
测试方法如下:
package cn.springdoc.demo;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import cn.springdoc.demo.cache.FooCahe;
@SpringBootTest(classes = SpringdocCacheApplication.class)
class SpringdocCacheApplicationTests {
static final Logger logger = LoggerFactory.getLogger(SpringdocCacheApplicationTests.class);
@Autowired
FooCahe foo;
@Test
void contextLoads() {
// 批量删除缓存
foo.clear();
}
}
执行测试,查看输出日志(省略了前面其他无关的内容),其中 type=KEYS
表示当前执行的 Redis 命令:
... Completing command AsyncCommand [type=KEYS, output=KeyListOutput [output=[], error='null'], commandType=io.lettuce.core.protocol.Command]
不要在 Redis 中使用 KEYS 命令 {#不要在-redis-中使用-keys-命令}
Redis 中的 KEYS
命令用于获取与指定模式匹配的所有键。尽管 KEYS
命令在某些情况下很方便,但它也有一些弊端。KEYS
命令需要遍历整个 key 空间来查找匹配的 key,它可能会导致 Redis 主线程在执行期间被阻塞(它是一个阻塞命令),这会影响 Redis 的响应能力和吞吐量,还会阻塞其他 Redis 客户端的请求。在有海量 key 的 Redis 数据库中使用 KEYS
命令是一种灾难。
所以,现在很多生产环境中的 Redis 都是禁止了 KEYS
命令的。
为了避免 KEYS
命令的弊端,推荐使用更适合的 SCAN
命令,它可以更高效地处理大型数据集,并提供更灵活的查询和操作选项。
配置 Spring Cache,使用 SCAN 来批量删除数据 {#配置-spring-cache使用-scan-来批量删除数据}
Spring Cache 提供了 RedisCacheManagerBuilderCustomizer
接口,允许我们以编程的方式对 RedisCacheManager
进行一些自定义。
通过这个接口,我们可以配置 Spring Cache,以使用 SCAN
来批量删除数据,如下:
package cn.springdoc.demo.configuration;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
@Configuration
public class CacheConfiguration{
@Bean
public RedisCacheManagerBuilderCustomizer RedisCacheManagerBuilderCustomizer(RedisConnectionFactory redisConnectionFactory) {
return builder -> {
builder.cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory,
// BatchStrategy 使用 SCAN 游标和多个 DEL 命令来删除所有匹配的键。
// 配置批量大小,以优化扫描批处理。
BatchStrategies.scan(100)));
};
}
}
配置完毕后,再次执行上述测试代码,查看控制台日志输出(省略了前面无关的日志内容):
... Completing command AsyncCommand [type=SCAN, output=KeyScanOutput [output=io.lettuce.core.KeyScanCursor@174cb0d8, error='null'], commandType=io.lettuce.core.protocol.Command]
如你所见,日志中的 type=SCAN
表示已经是使用 SCAN
命令来批量删除缓存项目了,配置成功。