事情发生在上月月初,前面想记录下拖到了现在。
1.事情记录
先说下客户的环境:
- 两套程序A(业务)、B(核对),分别也有两套对应的接口程序。主程序分别有两台负载。
- 库是再在一台服务器上oracle的两个用户,B对应库数据量比较大。
事故经过:
周一下午的时候客户那里A的一台服务器挂掉了,当时没在意因为一直运行好好的就让现场重启了。周二早上刚上班客户群里炸了A又有服务挂了!!!tomcat宕机假死了,虽然工作了这么多年现场应对还是还是有点匮乏。
说下当时的情况吧:
系统稳定运行很久了最近也没升级,经过对运维同事的询问这两天对系统的操作只有B系统的库在做数据迁移和索引重建这个是一个季度操作一次(表是按月份分区的一个季度大概有几千万的数据,迁移会消耗时间的,索引重建消耗时间相对小点,但是建的索引比较多,B系统是没停的事发当时看了数据库的锁是有锁的,所以这个操作他是在反复操作的) 。
为什么这里提到了B系统因为之前A系统客户反应过慢,在优化了B系统的数据库连接释放后A系统也正常了,B库的操作影响了A库的正常运行这个我拿不出有力的证据,这次很大程度上是B库的运维过程对A库造成了影响当然A系统有些地方也是有问题的。
2.问题需要立即的操作
- 切记立即重启丢失环境
- 找到tomcat对应进程 ps -ef|grep tomcat
- 得到当时的dump文件 jmap -dump:b,file=project.hprof pid
- 得到堆内存、GC的情况 jmap -heap pid >> jvm_memory.log
- 记录下当前数据库的连接数和锁
oracle
oralce的
select count(*) from v$process; --当前的连接数
select value from v$parameter where name = 'processes'; --数据库允许的最大连接数
SELECT SESS.SID, SESS.SERIAL#, LO.ORACLE_USERNAME, LO.OS_USER_NAME, AO.OBJECT_NAME 被锁对象名, LO.LOCKED_MODE 锁模式, sess.LOGON_TIME 登录数据库时间,
'ALTER SYSTEM KILL SESSION ''' || SESS.SID || ','||SESS.SERIAL#||'''' FREESQL
FROM V$LOCKED_OBJECT LO, DBA_OBJECTS AO, V$SESSION SESS
WHERE AO.OBJECT_ID = LO.OBJECT_ID AND LO.SESSION_ID = SESS.SID ORDER BY sid, sess.serial#;
3.问题排查
这里需要注意的点是需要判别给的假文件也就是正常文件可能是操作不当遇到的问题我已经遇到过两次了,需要有自己的判断。
3.1.内存问题
内存溢出的话tomcat会报错的,一般情况下会有OutOfMemoryError错误的。这个通过dump文件看下内存占比前面的且是自定义的那种。通过代码查找和结合当时的线程分析下问题所在的位置。
java
linux系统中
打开/tomcat/bin/catalina.sh文件
加上:JAVA_OPTS="$JAVA_OPTS -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/heapdump"
注:其中不设-XX:HeapDumpPath时,dump出的文件在/tomcat_home/bin目录下
windows系统中
打开/tomcat/bin/catalina.bat文件
加上:set JAVA_OPTS=%JAVA_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump
这个是前面写的笔记tomcat假死排查学习笔记
3.2.线程阻塞问题
-
如果是线程阻塞超出了tomcat最大线程数一般也会有错误信息的会报一个线程超数的错误。
这个需要分析下运行的线程为什么没有被释放掉,是不是在一个点上(客户的问题是卡在来库上了)。死循环、耗时操作、数据库连接问题。
耗时一般是需要优化的sql跟踪dump文件线程找到当时的sql放到库里面跑下,看是否需要优化(这里排查的问题日志打印的sql是未分页的感觉执行还行,但是找到的底层分页很慢)
-
还有个假死的这种情况啥也没有
这个我还没有找到具体的应对方法,需要看dum的线程去分析了
线程的一些状态
java
1.NEW(创建)创建态:当一个已经被创建的线程处于未被启动时,即:还没有调用start方法时,就处于这个状态。
2.RUNNABLE(运行时)运行态:当线程已被占用,在Java虚拟机中正常执行时,就处于此状态。
3.BLOCKED(排队时)阻塞态:当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态。当该线程持有锁时,该线程将自动变成RUNNABLE状态。
4.WAITING(休眠)休眠态:一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
5.TIMED_WAITING (指定休眠时间)指定时间休眠态:基本同WAITING状态,多了个超时参数,调用对应方法时线程将进入TIMED_WAITING状态,这一状态将一直保持到超时期满或者接收到唤醒通知,带有超时参数的常用方法有Thread.sleep、锁对象.wait() 。
6.TERMINATED (结束)结束态:从RUNNABLE状态正常退出而死亡,或者因为没有捕获的异常终止了RUNNABLE状态而死亡。
dump文件查看
- 系统自带的jvisualvm.exe
- eclipse的mat
QQL连接查询,看下当时的数据库连接情况
sql
SELECT OBJECTS ds FROM org.apache.commons.dbcp.BasicDataSource ds
系统访问慢的情况
- 1.找到慢的地方 可以统计请求的耗时,Nginx,Tomcat访问日志(access log)记录请求耗时
- 2.通过filter过滤器自己统计
- 3.请求耗时优化通过arths进行精确定位进行优化