2022-05-12 redis删除key

redis的key删除的时候,是一个阻塞操作;
为什么会阻塞呢,是在删除key的时候,首先要寻找的key,然后进行删除,然而当key已经过期了,或者被他人删除之后,在删除的时候,就找不到这个key,那么它就一直寻找,新版的redis 有最大重试次数(以前的版本直接死循环),那么就会导致一直阻塞.这对于一线上项目来说,阻塞的这段时间可能是致命的;

如何避免??
1 删除普通key,删除key的时候,可以提前判断是否存在,在的时候再进行删除,这样就可以很大概率减少此类事件发生,例如这样:

if (redisTemplate.hasKey(RedisConstant.CASCADE_CHECK_COUNT + cascadeDownTask.getId())) {
                redisTemplate.delete(RedisConstant.CASCADE_CHECK_COUNT + cascadeDownTask.getId());
       }

不要直接删除
2 其实还有一种情况,删除的这个key为大 key,什么是大key,不是key有多大,而是key对应的value 很大,例如 redis有五种数据类型,有四个是集合,那么当key对应的集合 非常大的时候,此key 就叫做大key,那么删除的时候,也会导致阻塞问题;

我们可以利用类如 分段处理的方式去处理,例如: 目前redis中对应的数据类型是hash,然后hash中有大量 hk ,我们可以分段去删除,然后每次删除休眠一下,这样可以有效减少redis的压力

@SpringBootTest
@Slf4j
class AreaDemoControllerTest {


//    @Autowired
//    IAreaDemoService areaDemoService;
//
//    @Autowired
//    AreaDemoMapper areaDemoMapper;

//    @Test
//    public void  aaa(){
//        List<AreaDemo> list = areaDemoService.list();
//
//        List<AreaDemo> areaDemos1 = list.subList(0, 10);
//        areaDemos1.forEach(demo -> demo.setId(null));
//        areaDemoMapper.updateBatch(areaDemos1);
//    }


    @Autowired
    RedisTemplate redisTemplate;


    /**
     * @Description 模拟批量删除大key
     * @Author FL
     * @Date 11:08 2022/5/12
     * @Param []
     **/
    @Test
    public void tetsts() {
        String key = "mapKey";

        redisTemplate.opsForHash().put(key, "NAME", "小明");
        redisTemplate.opsForHash().put(key, "age", "32");
        redisTemplate.opsForHash().put(key, "add", "西湖");
        redisTemplate.opsForHash().put(key, "tianqi", "晴朗");
        redisTemplate.opsForHash().put(key, "heart", "nice");
        redisTemplate.opsForHash().put(key, "shoot", "nike");
        redisTemplate.opsForHash().put(key, "sex", "男");

        Cursor scan = redisTemplate.opsForHash().scan(key, ScanOptions.NONE);
        int i = 0;
        while (scan.hasNext()) {
            Object next = scan.next();
            System.out.println(++i);
        }
//        redisTemplate.opsForHash().delete(key);
        removeBigKey(key, 2, 100);// 每次删除两个  休眠100ms
    }

    public void removeBigKey(String key, int scanCount, long intervalMills) throws CacheException {
        final ScanOptions scanOptions = ScanOptions.scanOptions().count(scanCount).build();
        // 避免内存泄漏
        // 执行循环删除
        List<String> fieldKeyList = new ArrayList<>();
        try {
            Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, scanOptions);
            if (ObjectUtil.isNotNull(cursor)) {
                while (cursor.hasNext()) {
                    String fieldKey = String.valueOf(cursor.next().getKey());
                    fieldKeyList.add(fieldKey);
                    if (fieldKeyList.size() >= scanCount) {
                        // 批量删除
                        Object[] fields = fieldKeyList.toArray();
                        redisTemplate.opsForHash().delete(key, fields);
                        log.info("[Big key] remove key: {}, fields size: {}", key, fields.length);
                        // 清空列表,重置操作
                        fieldKeyList.clear();
                        // 沉睡等待,避免对 redis 压力太大
//                        DateUtil.sleepInterval(intervalMills, TimeUnit.MILLISECONDS);
                        Thread.sleep(intervalMills);
                    }
                }
            }
            // 最后 fieldKeyList 中可能还有剩余,不过一般数量不大,直接删除速度不会很慢
            // 执行 key 本身的删除
//            redisTemplate.opsForHash().delete(key,fieldKeyList);
            redisTemplate.opsForHash().delete(key, fieldKeyList.toArray());
        } catch (Exception e) {
            // log.error();
        }
    }


}

代码均在 test中


版权声明:本文为qq_32419139原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。