Redis中的bigKey问题

在Redis中,"bigkey"是指占用较大内存空间的键(key)。对于bigkey的判定标准,《阿里云redis开发规范》中规定了:对于String类型value,value占用空间大于10kb,对于list,hash,set,zset类型若元素个数超过5000,就算bigkey。

bigkey有哪些危害?

① 超时删除,对于大key,删除是及其耗时的,由于redis命令的执行是单线程的,删除大key会导致主进程阻塞影响性能。

② 内存不均,集群迁移困难。

③ 网络流量阻塞

如何删除bigkey?

对于String类型的bigkey,不用del命令进行删除,使用unlink命令删除。

对于 list , hash , set , zset 类型的bigkey我们采用 hscan , sscan , zscan 命令渐进式删除。

scan命令:是一个基于游标的迭代器, 返回一个包含两个元素的数组, 第一个元素是用于进行下一次迭代的新游标, 而第二个元素则是一个数组, 这个数组中包含了所有被迭代的元素。如果新游标返回 0 表示迭代已结束。

list :采用 ltrim 进行渐进式删除,直到全部删除完成。

hash : 采用 hscan 和 hdel 命令分批次删除,使用hscan每次获取少量的元素(键值对),再使用hdel命令删除。

set :采用 sscan 和 srem 命令分批次删除。

zset : 采用 zscan 和 zremrangebyrank 命令批次删除。

为什么bigkey采用渐进式删除就可以减少删除操作的阻塞时间呢?

        在Redis中,所有的命令操作都是按顺序执行的,一个命令执行完成后才能执行下一个命令。当执行一个比较耗时的命令(删除大key)时,Redis主进程就会被阻塞,无法处理其他命令请求。

        通过将删除操作分批次执行,可以让每个批次的删除操作执行的时间较短,从而减少了每次删除的阻塞时间。在每个删除批次之间,Redis主进程有机会处理其他命令请求,包括读取、写入和其他操作。这样就实现了删除操作与其他操作之间的交替执行。通过合理的设置每个批次的删除数据量和删除间隔,可以让系统在删除大key的同时保持较好的响应性能,提高整体的吞吐量和并发处理能力。

        需要注意的是,虽然分批次删除可以减少删除操作的阻塞时间,但是仍然会有一定的性能影响。在删除大量数据时,仍然需要考虑系统的负载和响应时间,以避免过度消耗资源或影响用户体验。因此,需要根据实际情况进行测试和调优,找到适合系统的最佳批次大小和删除间隔。

bigkey删除案例:

 public void delBigHash(String ip,int port,String password,String bigHashKey) {
        Jedis jedis = new Jedis(ip, port);
        if (password != null && "".equals(password)) {
            jedis.auth(password);
        }
        ScanParams scanParams = new ScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
            List<Map.Entry<String, String>> result = scanResult.getResult();
            if (result != null && !result.isEmpty()) {
                for (Map.Entry<String, String> entry : result) {
                    jedis.hdel(bigHashKey, entry.getKey());
                }
            }
            cursor = scanResult.getCursor();
        } while (!"0".equals(cursor));

        //删除bigkey
        jedis.del(bigHashKey);

    }
    public void delBigList(String bigListKey,String ip,int port,String password){
        Jedis jedis = new Jedis(ip, port);
        if(password != null && "".equals(password)){
            jedis.auth(password);
        }
        long llen = jedis.llen(bigListKey);
        long left = 100;
        long count = 0;
        while(count < llen) {
            String ltrim = jedis.ltrim(bigListKey, left, llen);
            System.out.println(ltrim);
            count += left;
        }
        jedis.del(bigListKey);
    }

    public void delBigSet(String ip,int port,String password,String bigSetKey){
        Jedis jedis = new Jedis(ip, port);
        if(password != null && "".equals(password)){
            jedis.auth(password);
        }
        //每次扫描个数
        ScanParams scanParams = new ScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<String> scanResult = jedis.sscan(bigSetKey, cursor, scanParams);
            List<String> result = scanResult.getResult();
            if(result != null && !result.isEmpty()) {
                for (String member : result) {
                    jedis.srem(bigSetKey, member);
                }
            }
            cursor = scanResult.getCursor();
        }while("0".equals(cursor));

        jedis.del(bigSetKey);

    }
    public void delBigZset(String ip,int port,String password,String bigZsetKey){
        Jedis jedis = new Jedis(ip, port);
        if(password != null && "".equals(password)){
            jedis.auth(password);
        }
        //每次扫描个数
        ScanParams scanParams = new ScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<Tuple> scanResult = jedis.zscan(bigZsetKey, cursor, scanParams);
            List<Tuple> result = scanResult.getResult();
            if(result != null && !result.isEmpty()) {
                for (Tuple tuple : result) {
                    jedis.zrem(bigZsetKey, tuple.getElement());
                }
            }
            cursor = scanResult.getCursor();
        }while("0".equals(cursor));

        jedis.del(bigZsetKey);

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值