线上问题的解决与思路——如何对自己遇到的故障进行定位与解决?
一. 问题复现
今天我会结合自己的授信中心这个金融项目,继续给大家分析如何对自己遇到的故障进行定位与解决,希望本文可以对缺乏实际开发经验的小白有所帮助。
其实要想解决开发故障,通常的解决思路大致如下:
分析问题,根据理论知识+经验分析问题所在,并将错误锁定在一定的范围内;
通过错误日志,快速定位问题。线上定位问题时,主要是依靠监控和日志。
比如百泽老师遇到过这样一个问题:
线上的金融项目启动后,运行速度越来越慢,一段时间后直接无法访问,但此时的内存使用率正常,而CPU使用率几乎满负荷。在重启项目后,又运行了一段时间,项目重复出现该问题。
二. 解决思路
对于这种线上的故障,我们该怎么解决呢?其解决思路可以按照以下几个步骤来实现。
其实,大多数情况下,只要出问题,我们都可以利用 df(查看磁盘)、free(查看内存)、top(查看CPU) 来个素质三连,然后再通过jstack(Java堆栈跟踪工具)、jmap(Java堆和方法区的详细信息)等工具排查。这些工具的具体使用命令,大家可以自行查阅。
top命令
top命令或者其他监控数据,用于查看服务器的内存、cpu的使用情况。
jps命令
查看当前java程序的进程号,假如为:17357,
jstat命令
jstat -gc 17357 2000,可以查看jvm的内存分配情况,如图:
接着我们再通过查看EU和OU、YGC、FGC的变化,来调整jvm的内存、young区(edge,s1,s2)、old区内存大小。
可靠建议:
修改JAVA_OPTS='-Xms1024m -Xmx1024m' ,将jvm的最大、最小内存设为系统内存的3/4。根据ygc,调整young区中s与edge比例,根据fgc的频率调整young区和old区的大小(或比例)。
然后通过jstack 进程id,来查看线程的死锁,例如: jstack -l 21733 | more,若出现下图所示,则是出现了线程死锁。
tomcat优化
我们也可以在yml文件中tomcat的配置进行优化。
server:
port: 9105
tomcat:
threads:
处理请求的最大线程数
max: 350
最小的工作线程数
min-spare: 100
等待队列的最大队列长度
accept-count: 500
设置数据库连接池
对于数据库服务(如mysql),dba在部署的时候,都会设置db的最大内存和最大链接数,开发人员可以暂时忽略。
另外,数据库连接池请尽量别用dbch、c3p0等已经过时的连接池技术,推荐使用阿里巴巴的druid,其相关的链接配置,请参照其github的官网。
三. 具体解决过程
结合以上解决思路,接下来百泽给大家说一下我的具体解决过程。
top定位
由于cpu满负荷,所以我先通过top定位到出现问题的线程。发现确实是我们的java项目所在进程吃掉了所有的cpu资源,这时可通过jps+jstat来查看java的gc状态,进一步发现young gc几乎是一秒一次,fullgc没有。所以接着我又查看了jvm的设置,young内存才设为512m,我先去掉了这个配置,而是采用默认配置(young:old =1:2)。
在重启项目之后,young gc基本到了10多秒一次,可项目运行一段时间还是会卡死。然后我又看了下tomcat的连接池,发现全部都是默认配置(默认最大连接数为50),故先将最大链接设为500,等待队列设为1000,重启项目,还是出现cpu满负荷,系统卡死。
排查数据库服务的内存
接着我查看了输出的最新日志文件,发现日志输出到一个dao方法之后,在输出响应的sql后就卡住了。接着我通过mysql链接工具直接执行,sql却很快输出,因此排除数据库服务的内存不足等硬件问题。
调整日志输出级别
通过查看项目的框架,发现日志用的log4j(同步日志输出),日志输出级别是:INFO,这会导致项目里面的log输出非常多,所以我先将log的输出级别设为warn,重新启动项目,项目正常运行。
优化数据表
修改日志输出级别为warn之后,运行了一段时间发现系统又卡死了。这时还是两个表的查询卡死(通讯录和通话记录),这两个表的存储量级都是上亿级别的,项目原有的逻辑是这样的,用户上传通讯录,需要删除原来的通讯录,再批量插入。这样一个大表频繁的进行删除与批量插入,很容易导致IO响应慢。第一步,我先通过建立唯一索引,利用insert ignore into来减少该表的IO操作,接着重新启动项目,系统正常运行。
添加索引
加好索引优化了sql语句之后,系统还是偶尔会出现卡死状态。这时,我通过dba搜索慢查询,发现通话记录和通讯录表中有一个排序查询,该sql的末尾使用了order by update_time desc,但update_time没有添加索引,这就导致该表的查询至少要2秒以上。所以我将sql改为了order by id,查询就正常了,重启项目,系统正常运行。
解决线程死锁
至此,系统还是偶尔出现卡死现象(cpu爆表),只是频率小了很多。这时我怀疑是有线程死锁了,从而导致cpu爆表。我通过运维查找linux线程,终于发现确实有一个线程出现了死锁,里面的信息显示是c3p0的连接池线程。我又通过查找资料,发现使用c3p0作为数据库连接池,经常会出现链接池卡死的问题,所以我赶紧将项目的连接池切换为durid,然后重启项目,项目运行正常。
新增服务器节点
但系统在进件量较大时,依然有一定的几率出现卡死现象,最好考虑是当时的项目环境采用的是单机部署,所以最后协调运维新增了两个服务器节点,至此项目运行完全正常。
四. 总结
针对线上服务器故障的解决思路和过程,百泽老师给大家提供了以下几个可靠建议:
系统框架太过于老旧时,可能会引发一系列的项目问题,所以适当升级项目的技术是有必要的。
一些大表一定要进行拆分,否则在高并发的环境中,数据库的IO会遇到瓶颈。
减少一些不必要的日志输出,日志输出组件尽量是异步输出的。
一些高频率查询的字段,尽量加上索引、组合索引,一些慢查询的sql优化也很重要。
服务尽量单独部署。
对于线上服务器的问题解决和优化思路,现在你掌握了吗?
猜你喜欢LIKE
相关推荐HOT
更多>>servlet底层原理是什么?
1、ServletAPI核心类与接口2、Servlet类处理请求的流程创建servlet类的步骤:创建一个命名为TestServlet继承javax.servlet.http.HttpServlet类详情>>
2023-05-30 10:41:22多线程的优势与劣势分别是什么?
多线程是指在同一个程序中,同时运行多个线程,每个线程都可以独立执行不同的任务,相互之间不会干扰。多线程的优势和劣势如下:优势:提高程序...详情>>
2023-05-30 10:32:12设计模式之生产者与消费者的代码实现
本文主要讲述生产者和消费者模式,文中会使用通俗易懂的案例,使你更好地学习本章知识点并理解原理,做到有道无术。什么是生产者和消费者模式生...详情>>
2023-05-30 10:25:46从零开始学Java之interface接口
一.接口简介简介Java中的接口(interface)类似于是一种特殊的抽象类,它也是众多抽象方法的集合。接口的定义方式、组成部分都与抽象类相似,却比...详情>>
2023-05-29 11:26:17热门推荐
如何进行mysql数据备份?
沸什么是servlet的生命周期?servlet请求处理流程是怎样的?
热servlet底层原理是什么?
热怎样编写java程序?
新多线程的优势与劣势分别是什么?
ssm框架的作用与原理是什么?
设计模式之生产者与消费者的代码实现
接口和抽象类有什么区别?4个方面对比
从零开始学Java之interface接口
从零开始学Java之Java中的内部类是怎么回事?
一分钟带你了解MySQL——基础与介绍
在java中,super关键字怎样使用
什么是事件流以及事件流的传播机制 ?
弹性盒有哪些属性是在父元素身上?