ERR unknown command `KEYS`, with args beginning with:

1:问题描述

使用缓存注解@Cacheable进行缓存,@CacheEvict进行匹配删除时,由于条件是前缀 + *,于是发生了如下错误:

 

 

2:原因

这个错误已经说明了问题,就是你的redis禁用了keys命令

3:解决方案

3.1:重写源码的clean方法,把里面的keys改为scan

源码地址:org.springframework.data.redis.cache.DefaultRedisCacheWriter

重写源码的方法:我只把需要重写的复制过来了,其他的方法还是和源码一样的,直接把源码全部复制过来即可

/**
 * 缓存配置
 *
 * @author*/
@Slf4j
public class RedisCacheClearConfig implements RedisCacheWriter {
      @Override
        public void clean(String name, byte[] pattern) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(pattern, "Pattern must not be null!");
        execute(name, connection -> {

            boolean wasLocked = false;

            try {

                if (isLockingCacheWriter()) {
                    doLock(name, connection);
                    wasLocked = true;
                }
                long start = System.currentTimeMillis();
                // 使用scan命令代替原本的keys命令搜索key count(10000) 这个值不能太小,但是也不能过大,通常在5000-10000之间即可 源码地址:org.springframework.data.redis.cache.DefaultRedisCacheWriter
                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(new String(pattern))
                        .count(10000).build());

                Set<byte[]> byteSet = new HashSet<>();
                while (cursor.hasNext()) {
                    byteSet.add(cursor.next());
                }
                byte[][] keys = byteSet.toArray(new byte[0][]);

                if (keys.length > 0) {
                    connection.del(keys);
                    log.warn("redis缓存清理-数量:{}", keys.length);
                } else {
                    log.warn("redis缓存清理-无清理项");
                }
                log.info("redis缓存清理-执行时间:{}", (System.currentTimeMillis() - start));
            } finally {

                if (wasLocked && isLockingCacheWriter()) {
                    doUnlock(name, connection);
                }
            }
            return "OK";
        });
    }
  
}

重新赋值RedisCacheManager,需要把重写的设置给redis缓存管理

首先写一个配置类,里面增加如下方法:

    /**
     * @param redisConnectionFactory
     * @return 重写后的执行实现 主要是把clean方法中KEYS* 改为了scan的方式
     */
    @Bean
    public RedisCacheWriter redisCacheWriter(RedisConnectionFactory redisConnectionFactory) {
        return new RedisCacheClearConfig(redisConnectionFactory, Integer.valueOf(scanCount));
    }

设置给管理器

 @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {//重写后 改为scan的模式
        RedisCacheWriter redisCacheWriter = redisCacheWriter(redisTemplate.getConnectionFactory());
        CacheProperties cacheProperties = new CacheProperties();
    //过期时间 cacheProperties.setTtl(
600L); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())) .entryTtl(Duration.ofSeconds(cacheProperties.getTtl())); RedisCacheManager manager = new RedisCacheManagerResolver(redisCacheWriter, redisCacheConfiguration, getRedisCacheCustomConfiguration(redisTemplate, cacheProperties.getCustomTtl())); return manager; }

然后打断点,如果进入了,则说明重写成功!当然scan只是比keys要好一些而已。代码级别优化建议:一个是自己重写一个缓存逻辑,把查询的数据按照前缀 + id的具体方式存储,然后删除的时候,就直接使用具体的key而不是匹配模式

 

posted @ 2022-08-23 11:12  鸭猪是的念来过倒  阅读(5251)  评论(0编辑  收藏  举报