zoukankan      html  css  js  c++  java
  • Redis删除相同前缀的key

          如何优雅地删除Redis set集合中前缀相同的key?
          Redis中有删除单条数据的命令DEL,却没有批量删除特定前缀key的指令,但我们经常遇到需要根据前缀来删除的业务场景,那么究竟该怎么做呢?可能你一通搜索后会得到下边的答案:
    redis-cli --raw keys "prefix-*" | xargs redis-cli del

            直接在linux下通过redis的keys命令匹配到所有的key,然后调用系统命令xargs来删除,看似十全十美,实则风险巨大。这就是一颗随时爆炸的炸弹!我们都知道Redis是单线程服务模式,使用命令 keys * 查询key的时候会阻塞正常的业务请求,甚至造成redis宕机,所以肯定不行。因此,我们在生产环境中应当避免使用上边的方法,那使用什么优雅的方法来解决呢?SCAN!

    SCAN介绍

           Redis从2.8版本开始支持SCAN命令,它是一个基于游标的迭代器,每次被调用之后都会返回一个新的游标,用户在下次迭代时需要使用这个新游标作为SCAN命令的游标参数,以此来延续之前的迭代过程,直到服务器返回值为0的游标时,一次完整的遍历过程就结束了。
           SCAN命令的基本语法如下:
     
    scan cursor [MATCH pattern] [COUNT count]

    MATCH: 匹配规则,例如遍历以ops-coffee-开头的所有key可以写成ops-coffee-*,中间包含-coffee-的可以写成*-coffee-*cursor: 游标,

    COUNT:  COUNT选项的作用就是让用户告知迭代命令,在每次迭代中应该从数据集里返回多少元素,COUNT只是对增量式迭代命令的一种提示,并不代表真正返回的数量,例如你COUNT设置为2有可能会返回3个元素,但返回的元素数据会与COUNT设置的正相关,COUNT的默认值是10。
         下面在jedis中操作scan和keys,以删除相同前缀的key
     
    public void testSetDel(Jedis jedis) {
            try {
                log.info("---------------- Tests begin -----------------");
                initRedisData(jedis);
                String givenKey = "prefix_*";
                delValuesByKeys(givenKey, jedis);
     
                log.info("开始使用 scan 删除数据 ------------ ");
                initRedisData(jedis);
                this.delSetValues(givenKey, jedis);
     
                Set<String> keys = jedis.keys(givenKey);
                log.info("---------------- Tests end ----------------- 是否存在相同前缀的key result = " + !CollectionUtils.isEmpty(keys));
            } catch (Exception e) {
                log.error(" 删除指定前缀的key对应的键值对 " + e);
            } finally {
                if (jedis != null) {
                    jedis.close();
                    log.info("关闭jedis连接");
                }
            }
        }
        /**
         * java redis 删除指定前缀的key对应的键值对
         * @param givenKey
         * @return
         */
        public Boolean delSetValues(String givenKey, Jedis jedis) throws Exception {
            log.info("开始模糊删除set中的数据,givenKey = " + givenKey);
            List<String> keys = getByScan(givenKey, jedis);
            log.info("即将删除的key是 " + keys);
            String[] array = keys.toArray(new String[0]);
            jedis.del(array);
            return true;
        }
        /**
         * Jedis 删除指定前缀的key对应的key,使用keys
         * @param givenKey
         * @return
         */
        public Boolean delValuesByKeys(String givenKey, Jedis jedis) throws Exception {
            log.info("开始模糊删除set中的数据,givenKey = " + givenKey);
            Set<String> keys = jedis.keys(givenKey);
            for (String key : keys) {
                log.info("当前 key 是 :" + key);
                jedis.del(key);
            }
            return true;
        }
       private void initRedisData(Jedis jedis) {
            jedis.set("prefix_1333", "1");
            jedis.set("prefix_2KKKKK", "2");
            jedis.set("prefix_3哈哈哈哈哈哈", "777");
        }
     
    // 使用 scan
    public List<String> getByScan(String key, Jedis jedis) { List<String> list = new ArrayList<>(); ScanParams params = new ScanParams(); params.match(key); params.count(100); String cursor = "0"; while (true) { ScanResult scanResult = jedis.scan(cursor, params); List<String> eles = scanResult.getResult(); if (!CollectionUtils.isEmpty(eles)) { list.addAll(eles); } cursor = scanResult.getStringCursor(); if ("0".equals(cursor)) { break; } } log.info(" getByScan 查到的数据集是 ============ " + list); return list; }

          测试结果:

    ---------------- Tests begin -----------------
    开始模糊删除set中的数据,givenKey = prefix_*
    当前 key 是 :prefix_1333
    当前 key 是 :prefix_3哈哈哈哈哈哈
    当前 key 是 :prefix_2KKKKK
    开始使用 scan 删除数据 ------------
    开始模糊删除set中的数据,givenKey = prefix_*
    getByScan 查到的数据集是 ============ [prefix_1333, prefix_2KKKKK, prefix_3哈哈哈哈哈哈]
    即将删除的key是 [prefix_1333, prefix_2KKKKK, prefix_3哈哈哈哈哈哈]
    ---------------- Tests end ----------------- 是否存在相同前缀的 key result = false    
       总结:虽然不提倡使用keys命令删除key,但是,本文示例依然给出了示例,目的在于了解使用原理。当然,比使用scan命令删除key效果更好的方案是直接调用Lua脚本,童鞋们自己琢磨吧!
     
  • 相关阅读:
    ios开发--KVO浅析
    为iPhone6设计自适应布局
    详解iOS多线程 (转载)
    一些Iphone sqlite 的包装类
    ios多线程和进程的区别(转载)
    数据链路层解析
    物理层解析,交换机命令行
    计算机网络,数制模型
    java爬虫中jsoup的使用
    hadoop+zookeeper集群高可用搭建
  • 原文地址:https://www.cnblogs.com/east7/p/11665392.html
Copyright © 2011-2022 走看看