处理线上sql超时及报错

90 阅读3分钟

最近入职新公司,刚接手特别陌生,一个项目被拆分成了几十个模块,有时候找不到对应的模块,缺乏对应的文档查询,总之很不适合新手,开始接手的就是批量模块优化的问题,生产上报sql错及超时,优化就分配给我。

报错可以理解,但对于批量执行超时这个问题还真是很少见,通常是RPC调用超时才对,我们走进去内部看看,它是

update 表名 b set (b.字段1,,b.字段2) = 
(select a.字段1,a.字段2 from 表名 a where a.字段 = b.字段)	
where b.字段3 = ? and (b.字段4 = ? or b.字段4 = ?)

这个sql的意思是对a、b表联表查询出a表字段,并把这些字段根据条件补到b表中,光看sql暂时是没问题的,带到业务逻辑中看;

项目简况

首先项目是商户消费的,消费者不同的消费对应不同的渠道,此处是按有多少渠道就创建多少个线程,用countdownLatch来处理,我觉得从代码上说此处没有问题,而且不会出现上述超时;接着在各个渠道内部会通过fixedThreadPool再创建线程池,全渠道跑的话大概创建五个线程池,对应的存活线程数是100个左右(因为fixed创建的),我这边看数据库连接池数配置是80, 但我个人觉得一个批量模块相当于写死100个核心线程是不合理的,这里咱也不考虑队列满的情况,业务场景让它不会出现这种情况,以上是线程池的情况;

ExecutorService executorService = Executors.newFixedThreadPool(2);
 executorService.execute(()->{
        //逻辑
        });
countdownlatch.await();
executorService.shutdown();

报错解决

接着我们看业务,按商户来执行,一个商户号一个线程,此处依然用countdownLatch来处理,内部逻辑就是上面的sql;因为它只能update一条数据而我们的子查询可能是返回了多个行(多行的数据内容是一样的,子系统插入没校验导致重复我们模块被迫报错)导致报错,不给我进行限制查出来一条,无奈只能拆分,后面想想拆分也挺好,如果一个商户一天交易量10000,那么会出现数据库联表查并补全10000次;弄不好还真超时,而且代码不够美观;拆开后查询的结果我们放入缓存中,根据主键进行批量update;完美,

超时解决

那为什么会拆开还会超时呢,那是因为我们的查询并没有改变,条件依然复杂会全表扫,进而导致超时,而且会导致数据库连接池资源被耗尽最终拖垮整个程序;经商量,项目逻辑及sql条件不变,通过建立联合索引的方式来进行优化,最终测试结果是查询速度比之前快了三倍,最终是能满足当下的业务场景的;