业务背景
业务背景就是需要将多张业务表中的数据增量同步到一张大宽表中,后台系统基于这张大宽表开展业务,所以就开发了一个数据同步工具,由中间件采集binlog消息到kafka里,然后我去消费,实现增量同步。
生产环境发现的现象
在程序发布到生产环境时候,机器的CPU使用率立马被打到100%, load还在不停的上升,开始机器的配置是4核心8G内存,load可以到21,通过topc命令查看load average的三个值都大于20,说明机器非常的繁忙。
排查步骤及手段
1. 通过top命令查看到CPU使用率达到100%,但是内存却没有多大变化,内存变化可以通过gc日志可以观察的到。这时候配置文件中配置的是40个线程处理数据增量同步任务
2. 当时怀疑是不是线程数配置的多了原因导致的,所以将线程数调至为20个,但发现CPU依然是高位运行
3. 由于是生产环境,当时运维直接就建议升级服务器配置,有4核心8G内存升级到8核心8G内存。但是将程序部署到新服务器上后,新服务器的CPU使用率依然是100%,load也能达到20
4. 进一步将线程数调至为10个,CPU依然高位运行
5. 这是我就怀疑代码出问题了,top -Hp pid命令查看是那些线程最耗CPU,这里发现了一个奇怪的现象是:最耗CPU的线程数刚好10个,跟配置文件中配置的个数一样
6. 使用jstack命令查看thread dump日志,发现就是自己配置的10个线程的状态为Runnable,表示一直在那里运行。
排查到步骤6时,再结合第5点诡异的现象,回头再去看代码,在一个while(true)循环里不停的从队列中取数据,取到了则insert或update宽表, 取不到则立马进入下一轮循环,中间没有任何的sleep,导致系统会不停的调度该线程,占用CPU时间片,导致系统load飙升。
解决方法
在while(true)中如果从队列中取不到数据则sleep(1000), 加上这行代码之后,可以很明显的看到服务器的load在下降,知道下降到一个很低的值,因为这台机器的配置很高,只开了10个线程处理任务,负载当然很低。
空循环会导致CPU使用率很高
Linux系统中线程有几种状态:就绪状态,运行状态,阻塞状态,挂起状态,僵死状态。 Linux进程线程调度是对就绪队列中的线程进行时间片分配,阻塞状态和挂起都在阻塞队列中,只有唤醒之后才会被加入到就绪队列中等待内核的调度。
空循环虽然什么都没有做,也没有任何的阻塞条件(如sleep),进程一直处于运行状态,即使时间片一到被切换了,但是改进程还是处于就绪状态,等待下次调度。Linux内核调度是很复杂的,除了时间片之外还有优先级的权重,对于一直处于优先级的线程优先级会提高,这样空循环所在的线程分配的时间片的比重就会增多,导致系统负载上升。
至于空循环是如何影响CPU使用率和负载的,可以参考这边文章:https://www.2cto.com/kf/201601/488270.html