线上问题解决!
1.Thread pool is EXHAUSTED! (原因: 并发状态下,线程池不够用)
可以查这个网址: https://www.pianshen.com/search
解决:增加dubbo的线程数
<dubbo:protocol name="dubbo" port="-1" dispatcher="message" threadpool="cached" threads="${cdc_mbhk_loyalty_member_threads:800}"/>
=======================================================================================================================================================================================
2. 分库分表的时候,用mybatis拦截器改写sql问题,insert和update都可以在Interceptor拦截器拦截改写成功,但是select 查询还是会用拦截前的sql执行?
原因如下:
mybatis拦截器Interceptor其实是责任链模式,由于项目使用过程中使用了PageHelper,
导致向SqlSessionFactory中加入拦截器时不生效,解决办法是在springboot的启动类上排除掉PageHelperAutoConfiguration:
@EnableAutoConfiguration(exclude={PageHelperAutoConfiguration.class})
=======================================================================================================================================================================================
3.Thread pool is EXHAUSTED! (由于线上切换菲律宾的数据源,导致线程池一直hold保持连接,引发超过dubbo的最大线程数,导致服务雪崩)
解决方法:
第一:预警
第二:熔断
第三:分流
=======================================================================================================================================================================================
4.线上发现oom:java.lang.OutOfMemoryError
原因:
mybatisPlus框架: 有selectOne,getOne方法,但是底层都是会获取list,也就是说 会从mysql查出所有满足条件的数据。
selectOne方法: 期望查一条,如果超过一条数据就会抛异常。 Expected on but found ???
getOne方法: 只取第一条数据,如果查出多条,会抛出warn警告,不抛异常。 Warn: exectue method there are %* results
SqlHelper.getObject(this.baseMapper.selectList(queryWrapper));
原本以为getOne只查了一条数据,结果发现其实底层是去查了符合条件所有的list,这个list数据量又很大,有30多万条。导致内存不够,gc释放不过来。最终oom。
过程:
1.服务挂了之后,通过dump文件发现,内存有很多list,和HashMap占用了很大内存没有释放,并且数据量很大50多万笔。
2.并且发现了部分Mapper的sql查了大量的数据,但是不能确定是不是所有的sql查了大数据的都打出来了。
3.在服务加上拦截器,拦截执行的sql,并且打印出查出大量数据的sql,并且打出堆栈信息。
4.根据堆栈信息,定位到处问题的代码位置,并且根据getOne查看底层mybatis源码,发现以上问题。 修复之后,不再报oom。
extends BaseMapper<CustomerLoyMember> : 会有selectOne方法。
extends IService<CustomerLoyMember> : 会有getOne方法。
事故: 有一条sql通过mybatis来查询,原本需求只查一条符合的数据,但底层实际上通过条件查出了几十万笔数据导致oom:
//getOne
QueryWrapper<CustomerLoyCard> customerLoyCardQueryWrapper = new QueryWrapper<>();
customerLoyCardQueryWrapper.eq("MEMBER_ID", target.getMemberId());
CustomerLoyCard loyCard = customerLoyCardService.getOne(customerLoyCardQueryWrapper);
//selectOne
CustomerLoyContactSplit contactSplit1 = customerLoyContactSplit1Mapper.selectOne(customerLoyMemberQueryWrapper);
打印堆栈信息:用common-lang3 包中提供了 ExceptionUtils 类来帮助读取堆栈信息。
String stackTrace = ExceptionUtils.getStackTrace(new Exception("to print stack info."));
log.info("The stack info {}", stackTrace);
=======================================================================================================================================================================================
5.数据库发生死锁: Deadlock found when trying to get lock; try restarting transaction
查询mysql事务级别:select @@transaction_isolation;
问题原因:查出我们mbhk数据库的隔离级别是: 可重复读(repeatable read)。。wtcph用的的隔离级别都会选用读已提交(read commited)。
1.repeatable read存在间隙锁会使死锁的概率增大;在可重复读隔离级别下,条件列未命中索引会锁表!而在已提交读隔离级别下,只锁行;
2.在已提交读隔离级别下,引入了半一致性读(semi-consistent)特性增加了update操作的并发性能。
3.更重要的是读已提交的问题在开发中是可以接受的,毕竟你数据都已经提交了,读出来本身就没有太大问题。