项目场景:
提示:这里简述项目相关背景:
使用mybatisplus-dynamic的方法进行数据源切换:
DynamicDataSourceContextHolder.push(dynamicDsname);
执行完查询后也是进行了数据源的清除
DynamicDataSourceContextHolder.clear();
在生产环境中,我去切换其他数据源中查数据(业务需求:其他数据库是由其他公司进行维护,我们只能进行查询),可是没想到,一般也就是表中数据有问题,或者某个字段不符合,当时他们的表直接没有了,是的,直接整个表都没有了,然后就报错了,之后就是全部报错了。其他接口就是执行两次没问题,但是执行第三次就会有问题(整个系统都崩了)
问题描述
查看日志后发现都是一个问题,就是查询接口的时候使用的是切换数据源查询失败的那个数据源,但是页面接口都是两次正常,一次不正常(循环出错),把系统重新部署后就好了,我以为解决了,但是之后这个问题又出现了
原因分析:
1、并发情况下数据源之间切换有问题;
2、mybatisplus本身切换数据源有bug;
3、代码有问题,导致切换数据源失败;
4、代码中在切换数据源的时候加了事务;
分析一:
并发情况下上下文对象信息未及时同步,可能会出现这个问题吧,但是我们复现bug的时候,并发情况下其他接口是没有问题的(暂时搁浅)
分析二:
在查询mybatisplus的官网后,发现
这个版本是有点问题的,然后我就查看自己的版本,发现我的版本不在其中之列。
但是翻看源码发现
public final class DynamicDataSourceContextHolder {
private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedThreadLocal<Deque<String>>("dynamic-datasource") {
protected Deque<String> initialValue() {
return new ArrayDeque();
}
};
public static void push(String ds) {
((Deque)LOOKUP_KEY_HOLDER.get()).push(StringUtils.isEmpty(ds) ? "" : ds);
}
数据源是放在ThreadLocal中,这就有意思了,ThreadLocal是线程隔离,一次查询会有不同线程去查询,不同线程拿不同线程的数据源去查询数据。之前数据源报错,未关闭数据源,导致有一个线程Map中一直有这个数据源,之后其他接口只要用到这个查询的时候,就会使用那个切换数据源失败的数据源进行查询,然后就报错。
分析三、四:
代码是别人写的(整个系统95%的代码我写的,就这块功能是别人写的,我也没去管别人的代码,一直也没有报错,但是最终是我背锅,靠),没有用事务,就是一个简单的查询
解决方案:
提示:这里填写该问题的具体解决方案:
在执行切换数据源操作后,关闭数据源的操作放在finally代码块中,确保数据源会关闭成功。
但是有一个问题(只有表不存在的时候,报的xx.xx表不存在的时候才会出现这个bug),如果是sql有问题或者自己代码有问题,系统最终是报这个接口的问题,不会影响到其他接口(意思就是就算出bug了,数据源也是清除成功了),之前测试的时候也没有测出来这个bug(公司没有测试,开发就是测试),我也是没想到,一个表都能直接消失。(感觉这个bug处理得还是有点问题,没有找到最终的问题所在,现在没问题就行)