redis 连接打满的解决

Posted zerovpp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis 连接打满的解决相关的知识,希望对你有一定的参考价值。

先上效果,连接数从10000下降到25,提升400倍;

内存占用,从70%下降到18%,降幅1/4;

 

一、现象

服务用了redis,主要为加锁用,保证唯一性;最初服务正常,后期只要用到redis的地方,不超2分钟,10000的连接数就会被打满,最后造成资源耗尽、服务崩溃。

 

二、分析

阿里云redis最大连接数是10000个,被打满只有2个可能,连接数创建后,要么没释放(销毁),要么在持续不断的创建,且创建数无大于释放数。

怀疑1:连接没被释放

查代码,所有加锁的地方,最终都在finally中作unlock,所以此条被排除、

try
**业务代码**
  catch (Exception e) 
 finally 
            if (rLock.isLocked()) 
                if (rLock.isHeldByCurrentThread()) 
                    rLock.unlock();
       
   

怀疑2:连接在不断的创建,且大于销毁数

因为最初的redis是用默认配置,代码如下:

public class RedissonConfig 

    public static RedissonClient redissonClient() 
        Config config = new Config();
        String url = xx.getConfig("rds.url");
        String pwd = xx.getConfig("rds.pwd");
        Integer database = Integer.parseInt(xx.getConfig("rds.database"));
        config.useSingleServer().      .setAddress(url).setPassword(pwd).setDatabase(database);

        RedissonClient redisson = Redisson.create(config);

        return redisson;
    

以为是相关参数没配置,于是把参数加上,然并卵

 config.useSingleServer().setConnectTimeout(10000).setTimeout(3000).setIdleConnectionTimeout(10000)
                .setConnectionPoolSize(16).setConnectionMinimumIdleSize(6).setRetryAttempts(3).setRetryInterval(1500)
                .setAddress(url).setPassword(pwd).setDatabase(database);

后来查到这位博友:单例模式之「双重校验锁」_HoryC的博客-CSDN博客_单例模式双重校验锁

的文章,忽然顿悟,可能是JFinal在实现时,没有默认像SpringBoot一样将redis转为单例,造成不断创建线程连接,从源码可以看出,连接池默认是64,在旧锁未释放前,不断创建,造成打爆

三、解决 

知道原因就好办了,在创建前先作判断,如果存在则直接return,否则创建

public class RedissonConfig 
    //必须有volatile修饰(防止指令重排序)
    private volatile static RedissonClient instance;

    //构造函数必须私有(防止外部通过构造方法创建对象)
    private RedissonConfig() 
    

    public static RedissonClient redissonClient() 
        //第一个判空(如果是空,就不必再进入同步代码块了,提升效率)
        if (instance == null) 
            //这里加锁,是为了防止多线程的情况下出现实例化多个对象的情况
            synchronized (RedissonConfig.class) 
                //第二个判空(如果是空,就实例化对象)
                if (instance == null) 

                    Config config = new Config();
                    config.setCodec(new org.redisson.client.codec.StringCodec());

                    config.useSingleServer().setConnectionPoolSize(50);//设置对于master节点的连接池中连接数最大为500
                    config.useSingleServer().setIdleConnectionTimeout(100000);
                    //如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。
                    config.useSingleServer().setConnectTimeout(300000);//同任何节点建立连接时的等待超时。时间单位是毫秒。
                    config.useSingleServer().setTimeout(30000);//等待节点回复命令的时间。该时间从命令发送成功时开始计时。

                    //  不能反序列化 阻塞队列中的string元素
                    Codec codec = new JsonJacksonCodec();
                    config.setCodec(codec);

                    // 你的IP
                    config.useSingleServer().setAddress(url).setPassword(pwd).setDatabase(database).setKeepAlive(true);
                    instance = Redisson.create(config);

                
            
        
        return instance;
    

解决Oracle 11gR2 空闲连接过多,导致连接数满的问题

 

 

今天又遇到了11gR2连接数满的问题,以前也遇到过,因为应用那边没有深入检查,没有找到具体原因,暂且认为是这个版本Oracle的BUG吧。

上次的处理办法是用Shell脚本定时在系统中kill  v$session.status=‘INACTIVE‘的连接,但是这次现场没有在操作系统中部署脚本的权限,只好在数据库中做处理,幸好我们对这个 数据库有完全的权限。这次使用了profile+JOB定时alter system kill ‘sid,seral#‘ immediate的方式。具体脚本如下:

 

    1. CREATE PROFILE KILLIDLE LIMIT IDLE_TIME 30;

    2. SELECT * FROM dba_profiles WHERE PROFILE=‘KILLIDLE‘;

    3. ALTER USER TEST_USER PROFILE KILLIDLE;

    4. SELECT username,PROFILE FROM dba_users WHERE username=‘TEST_USER‘;

    5. ALTER SYSTEM SET resource_limit=TRUE;






    6. CREATE OR REPLACE PROCEDURE sp_kill_idlesession

    7. /**********************************

    8. 清除idle超时的会话进程

    9. **********************************/

    10. AS

    11. CURSOR c_kill_sqls

    12. IS SELECT ‘alter system kill session ‘‘‘||s.sid||‘,‘||s.SERIAL#||‘‘‘ immediate‘ sqlstr FROM v$session s WHERE s.STATUS=‘SNIPED‘;

    13. BEGIN

    14. FOR v_sql IN c_kill_sqls

    15.   LOOP

    16.     EXECUTE IMMEDIATE v_sql.sqlstr;

    17.   END LOOP;

    18. END;





    19. --添加JOB,定时清理过期会话

    20. DECLARE jobnum NUMBER :=661;

    21. BEGIN

    22.   dbms_job.submit(job => jobnum,

    23.                   what => ‘sp_kill_idlesession;‘,

    24.                   next_date => to_date(‘30-04-2014 18:00:00‘, ‘dd-mm-yyyy hh24:mi:ss‘),

    25.                   interval => ‘SYSDATE + 1/144‘);

    26.   commit;

    27. END;







    28. --如果30分钟过期时间太短,对数据库访问性能产生了影响,可以调整

    29. ALTER PROFILE KILLIDLE LIMIT IDLE_TIME 30;

以上是关于redis 连接打满的解决的主要内容,如果未能解决你的问题,请参考以下文章

深入理解分布式技术 - 分布式缓存实战_常见的坑及解决办法

redis系列之数据库与缓存数据一致性解决方案

微服务连接不上redis会直接访问数据库吗

"Redis客户端连接数一直降不下来"的有关问题解决

Redis

c#连接Redis缓存数据库与Redis的安装与配置