当前位置:   article > 正文

Redis删除、获取特定前缀key的优雅实现_redis获取指定前缀的key

redis获取指定前缀的key

一、数据量不大情况下,用keys

Redis的单线程服务模式,命令keys会阻塞正常的业务请求。
如果你一次keys匹配的数量过多或者在del的时候遇到大key,都会直接导致业务的不可用,甚至造成redis宕机的风险。
删除

     //删除所有以U:INFO:开头的key
     Set<String> keys1 = redisTemplate.keys("U:INFO:" + "*");  
     if (StringUtil.isNotNull(keys)) {
            redisTemplate.delete(keys);
     }
  • 1
  • 2
  • 3
  • 4
  • 5

查询

        // *号 必须要加,否则无法模糊查询        
        //String prefix = "ofc-pincode-"+ pincode + "-*";
        // 批量获取所有的key
        Set<String> keys = redisTemplate.keys(prefix);      
        List<MyObject> myObjectListRedis = redisTemplate.opsForValue().multiGet(keys);
        List<MyObject> myObjectList = JSON.parseArray(myObjectListRedis.toString(), MyObject.class);
        log.info("myObjectList:{}",myObjectList.toString());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

二、数据量大情况下,用scan

类似的SCAN命令,对于Redis不同的数据类型还有另外几个SSCANHSCANZSCAN,使用方法类似。
案例1

/**
 * redis扩展工具
 */
public abstract class RedisHelper {
    private static Logger logger = LoggerFactory.getLogger(RedisHelper.class);
 
    /**
     * scan 实现
     * @param redisTemplate redisTemplate
     * @param pattern       表达式,如:abc*,找出所有以abc开始的键
     */
    public static Set<String> scan(RedisTemplate<String, Object> redisTemplate, String pattern) {
        return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
                    .match(pattern)
                    .count(10000).build())) {
 
                while (cursor.hasNext()) {
                    keysTmp.add(new String(cursor.next(), "Utf-8"));
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                throw new RuntimeException(e);
            }
            return keysTmp;
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代 ,它们每次执行都只会返回少量元素,不会阻塞服务器, 所以这些命令可以用于生产环境, 而不会出现像 KEYS 命令、 SMEMBERS 命令带来的问题。

SCAN一样有它自己的问题:

1.因为是分段获取key,所以它会多次请求redis服务器,这样势必取同样的key,scan耗时更长。

2.在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。

案例2

   /**
     * 模糊匹配前缀删除缓存
     *
     * @param key
     * @return
     */
    @Override
    public Response<Void> fuzzyMatchDel(String key) throws CommonException {
        cacheAsyncTask.fuzzyMatchDel(key);
        return ResponseUtils.success();
    }
	
	
	
	
/**
 * @description: 缓存服务service实现
 */
@Service
@Slf4j
@EnableAsync
public class CacheAsyncTask {

    @Value("${component.cache.scanCount:100}")
    private Integer scanCount;
    @Value("${constant.cache.cacheInEquity:true}")
    private Boolean cacheInEquity;
    @Value("${component.cache.prefixCacheName:equity}")
    private String prefixCacheName;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public static final String SPACER = "::";
    public static final String WILDCARD = "*";

    @Async("asyncTaskExecutor")
    public void fuzzyMatchDel(String key) throws CommonException {
        log.info("fuzzyMatchDel删除开始,key前缀:{}", key);
        long beginTime = System.currentTimeMillis();
        // 校验传入的prefix
        this.regularCheck(key);
        // 通过spring-redis关于redis:scan命令相关api非阻塞查询缓存结果
        Set<String> keySet = stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> binaryKeys = new HashSet<>();
            ScanOptions scanOptions = new ScanOptions.ScanOptionsBuilder().match(key).count(scanCount).build();
            Cursor<byte[]> cursor = connection.scan(scanOptions);
            while (cursor.hasNext()) {
                binaryKeys.add(RedisSerializer.string().deserialize(cursor.next()));
            }
            cursor.close();
            return binaryKeys;
        });
        List<String> delFailKeys = new ArrayList<>();
        Iterator<String> it = keySet.iterator();
        while (it.hasNext()) {
            // 当前开发测试环境的redis版本不支持非阻塞删除unlink命令
            String keyNext = it.next();
            Boolean success = stringRedisTemplate.delete(keyNext);
            if (!success) {
                delFailKeys.add(key);
            }
        }
        long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - beginTime);
        if (delFailKeys.isEmpty()) {
            log.info("fuzzyMatchDel删除的keys:{},共{}个key,耗时{}秒", keySet, keySet.size(), seconds);
            return;
        }
        log.info("fuzzyMatchDel删除失败,扫描到的keys:{},删除失败的keys:{},耗时{}秒", keySet, delFailKeys, seconds);
    }

    /**
     * 对传入的key前缀进行校验
     *
     * @param prefix
     * @throws CommonException
     */
    private void regularCheck(String prefix) throws CommonException {
        if (cacheInEquity && !prefix.startsWith(prefixCacheName + SPACER)) {
            // 开启了只能删除权益宝工程创建的缓存
            throw ExceptionUtils.create(EquityException.EQUITY_CACHE_PREFIX_ILLEGAL);
        }
        if (!prefix.endsWith(WILDCARD)) {
            // 接口只做模糊匹配删除一类前缀的key
            throw ExceptionUtils.create(EquityException.EQUITY_CACHE_PREFIX_ILLEGAL);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

参考文章
https://www.cnblogs.com/37Y37/p/11037923.html
https://blog.csdn.net/qq_42643690/article/details/106234530
https://javazhiyin.blog.csdn.net/article/details/104890232
https://www.cnblogs.com/thiaoqueen/p/10919538.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/503450
推荐阅读
相关标签
  

闽ICP备14008679号