redis那点事9: 规范解决篇

redis大纲: 

    传送

 

我们先来复习一些命令:

    SCAN index [reg] [count]     迭代当前数据库的key

    SSCAN key index [reg] [count]   迭代集合的元素

    HSCAN key index [reg] [count]   迭代hash的键值对

    ZSCAN key index [reg] [count]   迭代zset的值和分数

 

    index 指针偏移量

    count默认10 也就是默认只查询10条

 

  在redis2.8以后增加了scan命令, 可以分批扫描redis记录, 会导致查询的时间很长,

  但是不会导致redis阻塞, 不会影响服务器的使用, 通常在删除情况下会用到

 

我们再复习一下keys * 所带来的问题:

    因为 redis 是单线程的, 如果在并发量很高的情况下, 如果使用 keys 会将所有请求阻塞, 

    等待 keys 命令执行完毕再处理, 并发量很高会导致 redis 服务器的宕机, 因为redis服务器宕机,

    所以请求全部并发到数据库, 造成数据库的宕机, 影响正常服务正常使用

 

    例子: 当QPS为200w的时候使用keys *操作:

        1. redis是单线程的, 用keys *命令查询所有的key, 导致redis被锁住

        2. 此时QPS非常高, 在keys * 检索的时候又来了很多个请求, 因为redis被锁住,

            所有的请求都被hang住了

        3. 因为很多请求被hang住了, CPU又飙升, 导致redis宕机

        4. 因为redis宕机, 请求全部并发到数据库, 导致数据库宕机

 

    使用高耗时的命令是很危险的, 会占用唯一一个线程的大量处理时间, 导致所有请求都被延迟 

    keys 命令的时间复杂度为O(n), 严禁在生产环境使用!

 

    还有很多时间复杂度为O(n)的命令如:hgetall lrange smemebers zrange sinter等命令, 并非不

    能使用, 需要指定N的值, 否则也可能出现缓存服务器宕机!

 

如何在生产环境禁用某些命令呢?

    在 redis 的配置文件中, 找到 SECURITY 这一模块

    

 

   通常在生产环境禁用:

        rename-command KEYS ""

        rename-command CONFIG ""

        rename-command FLUSHDB ""

        rename-command FLUSHALL ""

 

    注意:

        这些命令重命名后需要关闭 AOF 持久化, 不然会出现问题, 在配置文件中有明确的说明

 

下面使用 scan 进行产出操作:

     因为如果key很大, 删除需要很长时间, 会发生阻塞!

 

渐进删除list

    /**
     * 删除biglist  使用ltrim进行渐进删除
     *
     * @param jedis 连接
     */
    private static void deleteBigList(Jedis jedis) {
        String key = "biglist";

        // 获得list长度
        long len = jedis.llen(key);
        // 计数器
        int index = 0;
        // 每次删除多少个
        int left = 100;
        while (index < len) {
            // 每次从左侧截掉100个  保留left-len区间的元素
            jedis.ltrim(key, left, len);
            // 计数器增加
            index += left;
        }
        // 最终删除key
        jedis.del(key);
        System.out.println("删除完毕");
    }

 

渐进删除hash

    /**
     * 创建bighash
     *
     * @param jedis 连接
     */
    private static void createBigHash(Jedis jedis) {
        // 创建map
        Map<String, String> map = new HashMap<>();

        // 循环插值
        for (int i = 0; i < 2000; i++) {
            map.put(String.valueOf(i + 1), String.valueOf(i + 1));
        }

        // 批量添加
        jedis.hmset("bighash", map);
        System.out.println("生成成功");
    }

 

渐进删除set

    /**
     * 删除bigset  使用sscan+srem渐进式删除
     *
     * @param jedis 连接
     */
    private static void deleteBigSet(Jedis jedis) {
        String key = "bigset";

        // 遍历100条
        ScanParams scan = new ScanParams().count(100);
        // 指针
        String cursor = "0";
        do {
            // 进行scan遍历
            ScanResult<String> sscan = jedis.sscan(key, cursor, scan);
            // 结果集对象
            List<String> result = sscan.getResult();
            // 遍历删除
            result.forEach(e -> {
                jedis.srem(key, e);
            });
            // 获取到遍历完毕的指针
            cursor = sscan.getStringCursor();
            System.out.println("cursor: " + cursor);
        } while (!"0".equals(cursor));
        // 最终删除key
        jedis.del(key);
    }

 

渐进删除zset

    /**
     * 删除bigzset zscan+ztem
     *
     * @param jedis 连接
     */
    private static void deleteBigZset(Jedis jedis) {
        String key = "bigzset";

        // 遍历100条
        ScanParams scan = new ScanParams().count(100);
        // 指针
        String cursor = "0";
        do {
            // 进行scan遍历
            ScanResult<Tuple> zscan = jedis.zscan(key, cursor, scan);
            // 结果集对象
            List<Tuple> result = zscan.getResult();
            // 遍历删除
            result.forEach(e -> {
                jedis.zrem(key, e.getElement());
            });
            // 获取到遍历完毕的指针
            cursor = zscan.getStringCursor();
            System.out.println("cursor: " + cursor);
        } while (!"0".equals(cursor));
        // 最终删除key
        jedis.del(key);
    }

 

结束

  这就是我对redis规范解决篇的总结  感觉有用就点个赞吧 如果有错误或更好的方法评论区请多多指出  相互学习共同进步

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值