51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Cache 使用 SCAN 来批量删除缓存

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

赞(1)
未经允许不得转载:工具盒子 » Spring Cache 使用 SCAN 来批量删除缓存