在 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
命令来批量删除缓存项目了,配置成功。