Redis bigkeys命令会阻塞吗?怎么解决?

Posted 哪 吒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis bigkeys命令会阻塞吗?怎么解决?相关的知识,希望对你有一定的参考价值。


目录

专栏导读

🏆作者简介:哪吒,CSDN2022博客之星Top1、CSDN2021博客之星Top2、多届新星计划导师✌、博客专家💪 ,专注Java硬核干货分享,立志做到Java赛道全网Top N。

🏆本文收录于Java基础教程系列(进阶篇),本专栏是针对大学生、初级Java工程师精心打造,针对Java生态,逐个击破,不断学习,打通Java技术栈

🏆订阅后,可以阅读Java基础教程系列(进阶篇)中全部文章包含Java基础、Java高并发、Spring、MySQL等Java进阶技术栈

🏆还可以订阅其姐妹篇Java基础教程系列,包含全部Java基础知识点、Java8新特性、Java集合、Java多线程、Java代码实例理论结合实战,实现Java的轻松学习

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

🏆面试福音:10万字208道Java经典面试题总结(附答案)

大家好,我是哪吒。

上一篇分享了图解Redis,Redis主从复制与Redis哨兵机制,完成了Redis主从复制与Redis哨兵机制的学习和理解。

今天分享一次Redis引发的线上事故,避免再次踩雷,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。

一、 顺丰高级开发工程师在线执行了 Redis 危险命令导致某公司损失 400 万


一个命令损失数百万,这,需要赔偿吗?

代码不规范,同事两行泪,撸码需谨慎!

处于好奇考虑,我来测试一下,这到底是什么问题?

二、测试一下1000万数据的性能

1、编写脚本文件

写入1000万数据。

for((i=1;i<=10000000;i++)); do echo "set k$i 哪吒编程$i" >> /tmp/test1.txt;done;

通过/tmp/test1.txt查看一下是否写入成功。

2、写入Redis1000万数据

cat /tmp/test1.txt | redis-cli -a 111111 --pipe

3、通过keys * 查看1000万数据

4、通过配置文件禁止keys *的使用

在redis.conf文件中配置security:

  rename- command keys ""
  rename- command flushdb ""
  rename- command flushall ""

三、使用scan替代keys *

Redis Scan 命令用于迭代数据库中的数据库键。

SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。

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

scan语法:

SCAN cursor [MATCH pattern] [COUNT count]

四、拒绝bigkey

1、阿里云Redis开发规范

阿里云Redis开发规范中明确规定“拒绝bigkey(防止网卡流量、慢查询)”

String类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

2、出现bigkey时如何删除?

  1. String类型的用del删除。
  2. 其它类型使用hscan、sscan、zscan方式渐进式删除,同时要避免bigkey过期时间自动删除问题,因为它会造成主线程阻塞。

Hash 删除: hscan+hdel

public void delBigHash(String host, int port, String password, String bigHashKey) 
    Jedis jedis = new Jedis(host, port);
    if (password != null && !"".equals(password)) 
        jedis.auth(password);
    
    ScanParams scanParams = new ScanParams().count(100);
    String cursor = "0";
    do 
        ScanResult<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
        List<Entry<String, String>> entryList = scanResult.getResult();
        if (entryList != null && !entryList.isEmpty()) 
            for (Entry<String, String> entry : entryList) 
                jedis.hdel(bigHashKey, entry.getKey());
            
        
        cursor = scanResult.getStringCursor();
     while (!"0".equals(cursor));
    
    //删除 bigkey
    jedis.del(bigHashKey);

3、bigkey会造成哪些问题?

  1. 内存不均,集群迁移困难;
  2. 超时删除,阻塞线程;
  3. 网络流量阻塞;

4、如何发现bigkey?

(1)通过redis-cli --bigkeys查找。

(2)计算每个键值的字节数,通过memory usage key查找



Java学习路线总结,搬砖工逆袭Java架构师

10万字208道Java经典面试题总结(附答案)

Java基础教程系列

Java基础教程系列(进阶篇)

java 删除的Redis中的bigKey

    // Hash删除: hscan + hdel
    public void delBigHash(String host, int port.String password.String bigHashfCey) {
        Jedis jedis = new Jedis(host, port);
        if (password != null && !"" equals(password)){
            jedis.auth(passviord);
        }
        ScanParams scanParams = new ScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, sscanParams);
                    List < Entry < String, String >> entryList = scanResult.getResult();
            if (entryList != null && !entryList.isEmpty()) {
                for (Entry<String, String> entry : entryList) {
                    jedis.hdel(bigHashKey, entry.getKey());
                }
            }
            cursor = scanResult.getStringCursor();
        } while (!"0".equals(cursor));
        // 删除bigKey
        jedis.del(bigHashKey);

    }
    
    // List删除: ltrim
    public void delBigList(String host, int port, String password, String biglistKey) {
        Jedisjedis = new jedis(host, port);
        if (password != null && !"".equals(password)) {
            jedis.auth(password);
        }
        Long llen = jedis.llen(bigListKey);
        int counter = 6;
        int left = 100;
        while (counter < lien) {
            // 每次从左侧截掉100个
            jedis.ltrim(bigListKey, left, lien);
            counter += left;
        }
        // 最终删除key
        jedis.del(bigListKey);
    }
    
    // Set删除:sscan+srem
    public void delBigSet(String host, int port, String password, String bigSetKey) {
        Jedis jedis = new Jedis(host, port);
        if (password != null && !"".equals(password)) {
            jedis.auth(password);
        }
        ScanParams scanParams = newScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<String> scanResult = jedis.sscan(bigSetKey, cursor, scanParams);
            List<String> memberList = scanResult.getResult();
            if (memberList != null && !mefflberList.isEmpty) {
                for (String member : memberList) {
                    jedis.srem(bigSetKey, member);
                }
            }
            cursor = scanResult.getStringCursor();
        } while (!"6".equals(cursor));
        // 删除bigkey
        jedis.del(bigSetKey);
    }
    
    // SortedSet删除:zscan+zrem
    public void delBigZset(String host, int port, String password, String bigZsetKey) {
        Jedis jedis = new Jedis(host, port);
        if (password != null && !"".equals(password)) {
            jedis.auth(password);
        }
        ScanParams scanParams = new ScanParams().count(100);
        String cursor = "0";
        do {
            ScanResult<Tuple> scanResult = jedis.zscan(bigZsetKey4cursor, scanParams);
            List<Tuple> tupleList = scanResult.getResult();
            if (tupleList != null && !tupleList.isEmpty()) {
                for (Tuple tuple : tupleList) {
                    jedis.zrem(bigZsetKey, tuple ? getElenent());
                }
            }
            cursor = scanResult.getStringCursor();
        } while (!"6".equals(cursor));
        // 删除bigkey
        jedis.del(bigZsetKey);
    }

以上是关于Redis bigkeys命令会阻塞吗?怎么解决?的主要内容,如果未能解决你的问题,请参考以下文章

rdbtools分析redis bigkey

redis架构模式(6)数据倾斜

redis 阻塞原因

redis的incr和decr高并发会形成线程阻塞吗

Redis中的BigKey问题:排查与解决思路

Redis进阶学习10---redis最佳实践