我先说说自己碰着的情形吧。前段韶光做事器时时时地宕机,通过所有可能的日志文件,创造一个很奇怪的问题,便是数据库字段长度问题,以及类型不匹配问题。当时针对创造的问题进行了几次修正,可问题依然存在。当时刚好有个新功能上线,以是疑惑是这个新功能上线导致的。又想这些功能测试职员在测试环境都验证过了,以是定位为生产环境数据量大导致的问题。先看看日志输出的信息。
报错信息
缺点信息:ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值

这个缺点信息是说插入的信息长度太长,类型不匹配等等。这是一个修正状态的SQL语句爆出的信息,以是我们将这个状态对应的字段长度加长,当然问题还是存在的。又将修正状态的代码try...catch...了,问题还是存在。更令人费解的是,通过捕获非常,竟然涌现了id索引重复的情形,用的是oracle,id是用序列自增的。此时陷入了深深的焦虑!
我想如果再纠结报错的信息,肯定是不会有新的进展的。以是,此时的想法是不去管报什么错,直接从做事器排查,把它当做一个黑盒来对待。
我这里利用阿里的arthas来帮忙排查问题,也可以利用jdk自带的工具jmap、jstack等等。
1、dashboard 当前系统的实时数据面板
BLOCKED
创造了一个主要的信息,有个线程blocked了,些许的惊喜!
2、thread id 查看当前哨程信息,查看线程的堆栈
线程的堆栈信息
创造线程卡在一个叫runTrhead的方法那边。在一个线程中,竟然有多次调用。然后顺着这个思路去查找代码。
这是一个定时更新编码流水号的方法。方法的逻辑是这样,首先流水号是通过Redis的自增来得到的,确保在集群分布式下是唯一的,如果只是这样利用是没问题的。但是为了防止万一,从Redis获得到流水号之后,会定时将该流水号更新到数据库中。当Redis非常重启时就可以从数据库中获取最大的流水号,预防流水号从1开始,从而导致序号重复的问题。
那在更新流水号到数据库利用的是线程池 + 定时要求的办法。下面是代码的核心部分:
// 更新数据库策略:// 1、相差大小为N且韶光必须是大于t秒,则立即更新一次;(循环)// 2、或者韶光超过T秒,个数大于0个时也立即更新一次;(永劫光)if ( ((getCurrObjFromCache(role).getVal() - getReferObjFromCache(role).getVal() >= 10) && (System.currentTimeMillis() - getReferObjFromCache(role).getTime()) >= 3000)|| (getCurrObjFromCache(role).getVal() > getReferObjFromCache(role).getVal()) && (System.currentTimeMillis() - getReferObjFromCache(role).getTime()) >= 5000 ) {synchronized (role) {ValInfoCls cls = getCurrObjFromCache(role);ThreadPoolUtil.getFixedPool4Serial().submit(new MgrSerialroleThread(serviceImpl, service, role, cls.getVal()));// 同时把更新时的值赋给referMap,这个只是记录最新值,跟是否记录数据库不一定同等putObjToCache(referObj, role, cls.getVal());return;}}// 处理循环更新,末了一次的情形Thread.sleep(1000); // 递归调用runTrhead(role);
通过上面的代码,我们创造当知足更新数据库策略的条件时,才会去实行更新的操作——加入线程池中。当不知足条件时,休眠一秒,然后递归本身,直到知足条件为止。
以是我们就不难明得为什么在一个线程中,会多次调用同一个方法了。
而涌现BLOCKED,也正是由于并发调用导致数据库去世锁了。然后程序还一贯在实行,导致不断地开辟新的线程,或者加入到行列步队当中,从而导致做事器资源不敷,不能供应正常的做事。
为了防止涌现并发,而导致数据库去世锁。那么我们给它加一把分布式锁,
// 1、得到锁try {if(!serviceImpl.getCacheManager().getJedisTemplate().setnxex(key, val, 1)){return null;}} catch (Exception e) {logger.error("------------得到锁非常-----------key = {}, val = {}, e = {}", key, val, e);return null;}// 2、实行业务try {pubSerialRoleService.updateCurencoding(role, curdatetime, curencoding);} catch (Exception e) {e.printStackTrace();} finally{// 3、解锁serviceImpl.getCacheManager().safedUnLock(key, val);}
该代码并不能担保每次被调用,都能将最新的值更新到数据库中。涌现的几率是非常低的,并且从实际角度考虑大概可其存在,以是权衡之后就不再对这个进行优化。如果特殊在意的话,那么可以考虑优化,一个延长获取的韶光,但是这个不可取,会导致后面进来的延迟壅塞等等。其余一个方案是,将进来的值跟等待的值进行比较,如果是同一个role的,那么进行合并,取最新值去更新即可。