问题描述:
新版本灰度升级后,调用一个新接口,马上cpu会飙升到100+,并且实例再无响应,并且过几分钟后,cpu占用率会降下来,但是实例无法再提供服务,并且进程并没有死亡。
环境:
jdk:1.8
spring cloud: Daltson.RELEASE
排查办法:
- 1.查看此实例的进程号:
top
命令 - 2.查看此进程中占用最高的线程:
ps -mp pid -o THREAD,tid,time
- 3.将线程号转换成16进制:
printf "%x\n"
(上面命令看到的占用过高线程号) - 4.查看线程的堆栈信息:
jstack pid | grep tid
此时发现,占用最高的两个线程,全部是gc线程。 - 5.继续查看gc情况:
jstat -gcutil
,重启服务实例后,调用接口,cpu马上占用率飙高,用jstat命令查看到,这过程中fullGC达到300多次。明显此接口的一些操作引起了大量的对象堆积到内存中,导致内存爆掉了。 - 6.继续查看出现问题时,内存的占用情况,我选择查看内存中的对象的大小:
jmap -heap >heap.txt
jmap -histo > histo.txt
- 7.查看上面两个文件后终于发现了问题,调用此接口会产生大量的LinkedList对象和sharding-jdbc中的shardingPrepareStatment对象,这两个对象加起来将近占用了800m的内存,难怪会卡死。
问题原因:
后面就通过排查代码,跟踪数据,发现此问题是由于特殊的查询条件引起的sharding-jdbc出现了bug。
出现的原因如下代码示例:
<select id="selectByCondition" resultMap="BaseResultMap">
select ID, `NAME`, SEX , SHARDING_ID
from mem_user
<where>
1=1
<if test="id != null">
AND ID=#{id},
</if>
<if test="name != null and name != ''">
AND NAME=#{name},
</if>
<if test="ids != null">
AND ID in
<foreach item="item" index="index" collection="ids"
open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="sex != null">
AND SEX=#{sex},
</if>
AND COMPANY_ID = #{companyId}
</where>
</select>
查询条件:
Map<String,Object> param = new HashMap<String,Object>();
List<Long> ids = new ArrayList<Long>(1);
ids.add(null);
param.put("ids",ids);
param.put("companyId",1L);
List<MemUser> userList = this.userService.getByCondition(param);
可以看到上面的代码会产生select * from mem_user_1 in (null)
,这样的sql,只有此种情况下才会引起此问题。
公司使用的sharding-jdbc版本为2.0.0,后来自己回家用代码测试,因为我自己使用的是最新的3.0.0.M4,经测试已经无此问题。