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、问题的表象

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制造行锁伪造高并发造成的数据问题

druid连接泄露故障分析的更多相关文章

  1. 一次 Druid 连接池泄露引发的血案!

    最近某个应用程序老是卡,需要重启才能解决问题,导致被各种投诉,排查问题是 Druid 连接池泄露引发的血案.. 异常日志如下: ERROR - com.alibaba.druid.pool.GetCo ...

  2. 使用druid连接池的超时回收机制排查连接泄露问题

    在工程中使用了druid连接池,运行一段时间后系统出现异常: Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: ...

  3. Spring Boot下Druid连接池+mybatis

      目前Spring Boot中默认支持的连接池有dbcp,dbcp2, hikari三种连接池.  引言: 在Spring Boot下默认提供了若干种可用的连接池,Druid来自于阿里系的一个开源连 ...

  4. Druid连接池默认配置和坑

    一.公司默认配置 ds_0: !!com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: ...

  5. 使用druid连接池带来的坑testOnBorrow=false

    首先说一下自己程序中遇到的问题,前一段时间新写了一个项目,主要架构改进,为前端提供接口(spring +springmvc+mybatis) 在新项目中使用的是阿里的druid连接池,配置简单,除了数 ...

  6. druid连接池各属性说明

    转: druid连接池各属性说明 2018年10月17日 04:56:57 ht_kasi 阅读数:374   版权声明: https://blog.csdn.net/ht_kasi/article/ ...

  7. druid连接池获取不到连接的一种情况

    数据源一开始配置: jdbc.initialSize=1jdbc.minIdle=1jdbc.maxActive=5 程序运行一段时间后,执行查询抛如下异常: exception=org.mybati ...

  8. go的mgo,连接未释放问题,连接泄露。

    api启动几天后,卡住(连接失败,超时) 异常原因 mongo连接被占满,无法建立mgo连接,返回信息 查询点用端口可知,97%的连接被api项目占用. api项目的mongodb连接“泄露”,某处的 ...

  9. 【性能诊断】八、并发场景的性能分析(windbg案例,连接泄露)

    此前遇到一个项目反馈系统宕机问题,摘要描述如下: 系统不定期出现卡死现象,在多个模块不同功能上都出现过,未发现与特定功能相关的明显规律: 当系统出现卡死现象时,新的用户无法登陆系统: 跟踪应用服务器, ...

随机推荐

  1. YOLO v3 & Pascal VOC数据集

    代码地址:https://github.com/YunYang1994/tensorflow-yolov3 https://hackernoon.com/understanding-yolo-f5a7 ...

  2. git 合并分支 git merge branch_name

    * 查看分支 git branch * 更新 git pull * 切换到master分支 git checkout master Checking out files: 100% (907/907) ...

  3. python实现查找图片相同的id及重复个数

    import os #os:操作系统相关的信息模块 import random #导入随机函数 #存放原始图片地址 data_base_dir = r"C:\Users\Administra ...

  4. 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 百篇博客分析OpenHarmony源码 | v14.14

    百篇博客系列篇.本篇为: v14.xx 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有 ...

  5. docker 入门(docker 镜像 、容器、仓库)

    一.关于docker 镜像 .容器.仓库之间的关系 镜像(Image): 类似于虚拟机 的镜像 容器(Container): 类似于操作系统(或者说是独立的软件), 由镜像可以创建大量的容器. 仓库( ...

  6. 2021“MINIEYE杯”中国大学生算法设计超级联赛(8)(1002,1004,1006,1009)

    前言 依旧是白嫖账号,只打了一些题/kk 正题 1002 Buying Snacks 题目大意 \(n\)个物品,每个可以买一次也可以不买,如果买需要选择\(1/2\)块钱的,然后也可以相邻两个一起买 ...

  7. 牛客练习赛71E-神奇的迷宫【点分治,NTT】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/7745/E 题目大意 给出\(n\)个点的一棵树,每个点有一个选择权重\(a_i\)(有\(\frac{a_i ...

  8. python读Excel方法(xlrd)

    在我们做平常工作或自动化测试中都会遇到操作excel,Python处理exc相当顺手,如何通过python操作excel,当然python操作excel的库有很多,比如pandas,xlwt/xlrd ...

  9. 2021年3月-第02阶段-前端基础-HTML+CSS阶段-Day02

    HTML5 第二天 一.rotate 2d旋转指的是让元素在2维平面内顺时针旋转或者逆时针旋转 使用步骤: 给元素添加转换属性 transform 属性值为 rotate(角度) 如 transfor ...

  10. PAT (Basic Level) Practice (中文)1086 就不告诉你 (15分)

    1086 就不告诉你 (15分) 做作业的时候,邻座的小盆友问你:"五乘以七等于多少?"你应该不失礼貌地围笑着告诉他:"五十三."本题就要求你,对任何一对给定的 ...