druid连接泄露故障全面分析

 更新时间:2023年12月18日 09:48:41   作者:人工博客  
这篇文章主要介绍了druid连接泄露故障全面分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1、问题的如何发生的

1.1、应用功能介绍

系统是一个双数据源双写单独的服务。

(两个数据源是不同的存储,所以无法使用主从复制的模式,是一个切换存储介质的过渡态)。

历史代码有个更新逻辑update xx set a=b where m=n。

但是这个表中的记录超10亿。遇到需要更新的记录比较多的场景下存在问题。

故对这个进行了sql优化。采用的逻辑是查询出需要更新的记录id,然后分页更新。

1.2、关键代码

双数据源操作

private Object runSql(List<String> sqlSessionFactotyBeanNameList, MethodInvocation invocation)
            throws InvocationTargetException, IllegalAccessException {
        List<SqlSession> sqlSessionList = Lists.newArrayList();
        Object result = null;
        try {
            for (String sessionFactotyBeanName : sqlSessionFactotyBeanNameList) {
                SqlSessionFactory sqlSessionFactory =
                        RgApplicationContextUtil.getBean(
                                sessionFactotyBeanName, SqlSessionFactory.class);
                SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
                Object mapper = sqlSession.getMapper(invocation.getMethod().getDeclaringClass());
                Object[] param = invocation.getArguments();
                result = invocation.getMethod().invoke(mapper, param);
                sqlSessionList.add(sqlSession);//问题代码,注意!!!!
                sqlSession.commit();
            }
        } catch (Exception ex) {
            sqlSessionList.stream()
                    .forEach(
                            x -> {
                                x.rollback();
                            });
        } finally {
            sqlSessionList.stream()
                    .forEach(
                            x -> {
                                x.close();
                            });
        }
        return result;
    }

问题的sql

<select id="getBatchIdWithLimit" resultType="java.lang.Long">
	SELECT x.id FROM context x WHERE x.oid = #{oid} ORDER BY id ASC
	LIMIT #{offset}, #{limit}
</select>

关键的配置

maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。

当前系统此参数未进行配置,所以会无限等待,使用的是公平锁

1.3、问题出现的步骤

  • sql中存在问题,部分数据的长度超过Integer的最大值(2147483647),映射存在问题。
  • 双数据源代码存在bug。 List的代码结合的 add 位置过于落后,导致反射出现异常的时候。当次的SqlSession未关联到待处理的集合中,进而也就未rollback和close。造成链接泄露。
  • 当出现问题的数据的时候,结合双数据源的代码的bug。会造成List为空,所以未进行释放操作,(链接泄露了)
  • 当前系统最大的连接数是100,出现了100次这样的数据,这个服务就回无尽的等待获取链接中的状态。

1.4、问题的表象

image-20210601165753462

代码的问题点

2、如何复现问题

2.1、问题数据复现

  • 把数据库的最大连接数调整成1,maxWaitTime不设置
  • 构造一条id大于2147483647的数据
  • 使用api 触发调用到这个逻辑
  • 结果是:第一次调用报错,第二次调用会卡的客户端设置的超时时间。

2.2、数据库连接异常复现

还有一种路径是代码都没问题,但是由于高并发造成数据库是锁。mybatis是可以设置sql的执行时长的。一旦出现了这种场景。问题也是会出现的。

但是这种场景比较难以复现,那么有没有一种手段可以高效的伪造这个场景。

准备知识

set autocommit=0;  //关闭数据的事务自动提交
SELECT * FROM xxx a WHERE a.id='111' for update; //获取数据库的行锁
commit;//提交事务

数据默认是自动提交的,所以前置set autocommit=0;这个操作不要忘记了,踩过几次坑。完成后执行commit;进行解锁。

测试完毕记得set autocommit=1;来恢复数据库的事务自动提交的特性。

  • 准备一条接口测试用的数据
  • 执行sql select …for update 进行行记录锁定
  • 接口调用使用同一个id进行请求。因为记录锁定了,所以api的更新是失败了,成功的伪造了高并发形成了行锁造成的sql问题

3、问题总结

  • 数据库的保护配置:maxActive、maxWait都配置上,相当于熔断保护
  • mybatis对象映射需要关注数据的范围
  • 利用select for update制造行锁伪造高并发造成的数据问题

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Springboot详细讲解循环依赖

    Springboot详细讲解循环依赖

    最近在使用Springboot做项目的时候,遇到了一个循环依赖的问题,所以下面这篇文章主要给大家介绍了关于springboot循环依赖实现以及分析的相关资料,需要的朋友可以参考下
    2022-06-06
  • 详解Java 中泛型的实现原理

    详解Java 中泛型的实现原理

    这篇文章主要介绍了详解Java 中泛型的实现原理,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • 聊聊Object类中的wait()和notify()方法

    聊聊Object类中的wait()和notify()方法

    这篇文章主要介绍了Object类中的wait()和notify()方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 深入理解Spring Cloud Zuul过滤器

    深入理解Spring Cloud Zuul过滤器

    这篇文章主要给大家介绍了关于Spring Cloud Zuul过滤器的相关资料,通过阅读本文您将了解:Zuul过滤器类型与请求生命周期、如何编写Zuul过滤器、如何禁用Zuul过滤器和Spring Cloud为Zuul编写的过滤器及其功能,需要的朋友可以参考下。
    2017-02-02
  • Java Lambda表达式之从集合到流

    Java Lambda表达式之从集合到流

    这篇文章主要介绍了Java Lambda表达式之从集合到流知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • Servlet+JDBC实现登陆功能的小例子(带验证码)

    Servlet+JDBC实现登陆功能的小例子(带验证码)

    这篇文章主要介绍了Servlet+JDBC实现登陆功能的小例子(带验证码),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Java常用的数据脱敏方法(手机、邮箱、身份证号)

    Java常用的数据脱敏方法(手机、邮箱、身份证号)

    这篇文章主要给大家介绍了关于Java常用的数据脱敏(手机、邮箱、身份证号)的相关资料,信息脱敏对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护,需要的朋友可以参考下
    2023-07-07
  • PowerJob的IdGenerateService工作流程源码解读

    PowerJob的IdGenerateService工作流程源码解读

    这篇文章主要为大家介绍了PowerJob的IdGenerateService工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 详解SpringBoot配置devtools实现热部署

    详解SpringBoot配置devtools实现热部署

    本篇文章主要介绍了详解SpringBoot配置devtools实现热部署 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Java线程池源码的深度解析

    Java线程池源码的深度解析

    线程池的好处和使用本篇文章就不赘叙了,这篇文章主要通过线程池的源码带大家深入了解一下jdk8中线程池的实现,感兴趣的小伙伴可以了解一下
    2022-10-10

最新评论