项目中常见BUG的排查方法汇总

1 前言
最近各项目组频繁出现技术问题,问题主要包括:测试不达标、生产事故两大类。常常有人问我寻求技术支持,其中包括排查办法、思路、有事直接参与技术排查过程中。怎奈我一人能力有限,无法满足整个DB部门的需求,痛定思痛,决定总结此文,以纪念过去十余年来踩过的坑。
2 需要的技术知识
计算机技术是一门系统的科学,任何一个点上出现的问题都有可能导致全局性的问题,我们有时需要从计算机硬件、网络硬件、数据库、各种中间件技术及工作原理、算法、编程语言技术特点、业务需求、架构设计等各个方面进行综合分析才能判断和定位问题。以上知识的学习过程有可能很漫长,不是一个人力所能及,所以排查复杂问题的过程需要各方面的专家综合会诊才可以解决。所以这也是个团队合作的过程。
3 需要的材料
问题排查的过程就像福尔摩斯探案一样,不能放过任何蛛丝马迹,并作科学的分析与论证才能定位问题的本源。虽然并不是我们面对的所有问题都很复杂,但是当我们有更多的信息时有助于我们更加快速准确的定位问题。更具上面我们提到的知识来说,我们定义复杂问题所需的资料一般包括但不仅限于:系统设计文档、业务流程、应用和各种组件的日志、Dump信息、硬件配置单、网络部署图等内容以及系统问题现象描述。
4 观察现象:
系统当前展现出的问题现象往往是我们确定系统问题的第一手资料。这些感官现象包括:系统慢、宕机、夯死等现象,同时监控系统也会伴随出现警告或者错误信息提示。但是这些信息仅仅是出于感观方面的,不能定位和发现问题起到指导作用,为了发现问题我们需要从以下几方面进一步观察细节。
4.1 硬件类:
事实上大部分的故障排除过程中,硬件问题往往具备以下几个特点:
**1、**最容易发现的;通过几个命令即可快速查看状态,并发现和定位问题;除非他是被间接影响的。
**2、**最容易被忽略的;往往由于硬件原理理解不够,或者是开发者的惯性思维将问题优先假设在代码层面上。
硬件类问题主要观察的指标包括CPU使用率、物理内存占用率、SWAP占用率、网络IO占用率、磁盘IO占用率等等;比较好用且全面的检查工具是nmon,如果没有安装的情况下可以使用其他命令进行综合排查。
一般情况下硬件类指标的影响都是伴随型出现的,但是我们通过单个特征的特点,进行综合性分析。一般情况下单纯的CPU占用率高主要是由于算法复杂,运算量大,逻辑判断多等情况导致;物理内存占用率高,如果是对于java程序由于其xmx参数的特点,可以不用过度关心,此时主要看SWAP内存占用率情况,如果这个值很高的话会导致系统运行缓慢;网络IO占用率高,分为读/写两种,这主要是数据交换频繁导致,;磁盘IO占用率分为读/写两种,要区分是哪个值高,然后通过程序设计的特点排查和定位问题。
4.1.1 虚拟机参数:
大部分程序员第一感觉是虚拟机跟物理机一样,但实际上受到虚拟化技术原理的限制,会有一些我们意想不到的问题。通常我们见到的硬件类常见问题以外,虚拟机环境也同时受到了所在物理机各种参数的限制,而出现各种各样的问题;最常见的问题就是一台物理机上部署了多个虚拟机,此时在高并发情况下,物理机出现整体的性能瓶颈。
在使用虚拟机遇到性能瓶颈时,建议结合虚拟机所在的物理机的磁盘io以及网络io配合进行检查。因为一台物理机上往往部署多个虚拟机,对于有条件的系统建议将一些网络带宽和磁盘io占用较大的虚拟机尽量随机部署在硬件设备上,防止个别物理机资源过载。
4.2 网络设备及负载均衡情况:
一般情况下我们用到的网络设备和负载均衡设备不会出现问题,除非出现了网络限流;负载均衡设备使用了不合理的参数配置也会导致问题,如session的绑定,以及负责均衡的算法规则上。
网络设备大部分都具备抓包功能,如果遇到网络问题,可以配合抓包工具帮助定位问题。
4.3 JVM类:
4.1.2 JVM内存:
由于JVM的版本和厂商不同,默认采用的垃圾回收器算法也不尽相同,而且的JVM内存管理机制可以更换,因此这里不过多加以论述,但是对于问题排查方式大致相同。JVM内存常见问题主要有两种,一是由于内存的堆占用过大多导致的Full GC次数过于频繁,表现是程序吞吐量降低,性能逐渐下降。二是JVM内存溢出,表现是直接抛出内存溢出的异常。这两种现象的出现实际上是伴随出现的,一般情况下Full GC会先出现,之后转为内存溢出。主要是看内存泄漏的速度,如果速度较快full GC的过程就会很短,如果速度慢,则会表现出很长一段时间的性能低下。检查办法有两个:
1、oracle jvm一般使用jmap查看jvm堆当前状态,统计被实例化数做多的类和占用空间最多的类型。
2、通过使用GC日志,查看Full GC次数与频率
4.1.3 Jvm 线程状态:
Java线程状态包括一下几种:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。一般情况下具备潜在风险可能的主要是BLOCKED和WAITING,主要表现为系统吞吐量低,运行缓慢。可以使用jstack查看线程当前运行状态。
4.4 数据库类:
数据库出现问题一般都是比较严重的技术问题,这种情况下会出现大面积的应用不可用或者服务缓慢,建议让DBA配合进行排查。除此之外以下数据库方面的检查可能会帮助定位到问题的原因:
1、 查看数据库的活动的session数量,及持续时间,可以统计相关的SQL执行时间
2、 查看数据库索引状态是否失效的
3、 查看数据库CPU是否高,说明有大运算量的SQL在执行,这种运算量可能是SQL逻辑复杂、全表扫描、没走索引、数据量大等情况时出现
4、 使用SQL执行计划查看SQL的执行成本,一般是走了不该走的索引,或者是没走索引导致
5、 检查数据库中是否有大量的锁
6、 检查相关sql对应的表中的数据量是否过大
4.5 消息队列:
消息队列除了出现明显的服务宕机情况以外,大部分情况下的表现都是消息积压。我们可以通过观察一下现象协助排查问题
1、 查看消息积压情况,是否积压是持续增长的,用于判定情况是否会有好转的可能。
2、 检查积压的消息内容是否是某一类型的业务,用于确定这是否是某一个特殊的写法导致的,以及判定问题的影响范围。
3、 检查MQ的日志看是否收到了ACK应答,用于判断是否是程序本身没有签收导致。检查ACK超时时间等参数,是否配置不合理,导致签收超时
4、 检查生产者及每秒钟生产消息的数量,检查消费者及每秒钟消费消息的数量,判断是否是生产过快,消费过慢导致。
5 排查过程:
5.1 保护现场:
我遇到过的很多问题,不能在第一时间定位到故障原因,主要是因为出现了问题以后现场被破坏导致,导致这个问题的主要原因是:
1、由于不好定位或者是因为生产系统要快速恢复运营,系统马上被重启了;
2、自以为需要的资料已经采集全了,快速重启了系统。
3、由于日志打印不全,认为已经没有有价值的材料可以收集了,快速重启系统。
其实现代系统已经在HA方面做了很多工作。在出现问题的情况下,只要系统整体能够提供服务的情况下,没必要马上进行重启,大部分异常情况下是可以通过引流和隔离的方式,将流量引入正常节点,为排查和分析过程留下更多的分析时间。
一般情况下,我们需要采集的资料包括但不限于:应用日志、jvm线程和内存的dump文件(每隔10-30秒截取一次,建议截取3次)、中间件、数据库日志、cpu、物理内存、网络带宽、磁盘io等。
5.2 排查问题
系统出现的问题基本可分为容易排查型(急症)和不容易排查型(慢症)两种。急症类问题一般情况下日志中的异常和系统异常的现象是直接相关联的,病症好定位、好排查。慢症类问题一般只能看到崩溃或者是系统运行缓慢,这类问题只通过日志很难排查定位。不论是急症还是慢症,我们要从异常的现象和日志中通过系统的方式进行摸排,缩小可疑范围,找到问题的根源。
5.2.1 分析及定位过程
5.2.1.1 步骤一 日志检查:
系统出了问题我们一般首要排查的就是日志。绝大多数情况下的,在系统出现问题时都能找到一些如exception,除非日志打印不规范,把exception都吃掉了。这种情况下我们一般直接根据日志定位问题即可,如果能够直接定位问题的我们一般都可以定义为急症,这种情况下直接制定解决方案即可。
5.2.1.2 步骤二 资料收集与准备:
有些问题只通过日志排查不太好定位,需要靠复杂的分析过程才能找到问题。分析过程建议要自上而下从系统设计入手,采用隔离法、替换法、对比法、排除法来进行。此时需要升级排查手段。
对于内存溢出类的问题定位我们主要收集的材料包括内存溢出的频率周期、批量任务还是联机任务等信息,使用到的算法、详细设计;
对于系统运行慢等问题我们需要收集的资料包括部署方式、硬件及使用情况、使用到的算法、详细设计、缓慢的时间段、时间点、交易特点等;
另外对于现有现象、及已经做过的各种测试及结论,要请项目组进行特别说明;
最好要准备一套测试环境,能够进行模拟和还原现象,用于验证问题点,及问题点排障后的效果
5.2.1.3 步骤三 分析与计划:
分析的过程要采取大胆假设,仔细论证的指导方针。在未经实验论证或者专家团有统一结论前,不要过早的排除任何可疑点。
第一步、我们需要将系统根据软件和硬件、子系统和模块的接口将系统进行划分;划分的过程主要是为了方便排查过程中使用替换法、排除法、加压减压法、代码走查法进行定位。对于复杂的系统,建议先做粗粒度的划分,能有效的缩减排查范围以及方便的进行隔离为前提。排查之后,可以对某一怀疑部分进一步形成更细粒度的划分。
第二步、对划分好的区域范围建立各种假设,最初的假设可以范围大一些,可以在项目组中或专家团中征求全部的意见,然后进一步划分成更小的部分。
5.2.1.4 步骤四 执行排查:
执行排查之前建议将所有可疑点进行优先级排序,优先级按照可能性进行排序,可能性越高的优先排查。
首先、结合现有已知的现象进行分析,将前面列举的可能存在的假设点进行排除;
其次、对优先级高假设点,优先安排测试计划,验证其有问题或者没问题,以逐步缩小排查范围;
最后、经过测试采集新的现象,并进行分析,之后调整假设点;
排查常用的方法:
1)、替换法:可以通过替换的过程,观察替换前与替换后的情况,判定被点换的部件是否存在问题;
2)、排除法:对于有透传或者一些通过配置可以摘掉的组件,可使用将其从系统中摘除后观察其结果,判定其是否存在问题;
3)、加压法或减压法:通过增加压力或者减小压力的办法加速测试结果的复现,如增加用户数、减少等待的时间间隔、减少资源数量等方式,使怀疑点的状态加以凸显,进行排除;
4)、代码走查:对可疑代码组织专家团进行代码走查;

5.2.1.5 步骤五 得到结论:
反复执行步骤一到步骤四,直到找到一个假设点是导致整个问题的主因。此时注意,不要因为找到这个主因就盲目下结论,进行修改。因为很多时候我们见到的现象是由一连串复杂事件组合导致的。我们需要判断是否可以从原理上解释通我们之前收集到的所有现象,如果发现仍有现象无法解释通,那么此怀疑点可以做为一个间接影响因素,可以在此基础上缩小假设范围继续进行排查指导找到主因。并形成问题排查差报告。
5.2.2 常用排查技术及注意事项
5.2.2.1 日志排查的技巧;
1、 日志排查最直接的办法是在应用日志中搜索exception或者error字样,不要漏掉任何exception。
2、 如果应用日志中没有打印以上信息,那就去weblogic、tomcat等容器的日志中去找,如果还没有就去这些容器启动时转输出用的nohup.out中去找
3、 如果这几个位置都没有明显的exception,那么最大可能就是代码中给吃掉了。
4、 有时我们可以看看warning级别的日志,一般有些框架捕获到异常后有些会以warning级别的日志打印,这也是重要线索。常见的如文件系统空间不足,一般都是warning级别的日志。
5、 日志排查中对于可疑的交易,最好抓出来,按照线程号搜索一下交易从入口开始到结束时的所有打印信息。这可能会发现哪些交易中间卡住了。或者说那两个步骤之间执行的缓慢耗时大。
6、 如果仍然定位不出问题,不要只排查问题出错那一刻的日志,要前推排查1天内的日志或者从现象开始以后的所有日志。
7、 日志中如果出现很多异常信息,往往会只关注了最后的一个异常信息,其实很多时候是最后一个日志信息并不是主因,而是一种表象,这里我们要遵循时间线的关系,我们要根据错误发生的时间点,往前推,包括日志没打印,结合错误信息发生的时间点综合分析,找到问题根源。
注意:不仅排查应用日志、要一起配合排查数据库、中间件等一起关联系统和关联组件的日志。因为由于系统架构的特点导致,现象是有关联关系的。这有助于我们分析和定位主要原因。
5.2.2.2 Jvm Dump文件的技术排查:
Jvm的dump主要有两大类,上面我们已经做了介绍,因为dump技术是快照技术,只能显示当前时刻下系统的情况,建议采集dump文件时要连续采集多次,建议是时间间隔是10-30秒一次,共采集3-5次。分析dump文件不要只看一次采集的结果,要结合采集到的3-5次dump进行横向比较jvm状态。
1、 通过dump文件主要留意dump中年老代占用的比例。如果年老代占用比例走高,或者已经到了90%以上都有可能出现内存溢出。
2、 另外通过jmap命令可以查看当前内存中创建的对象数目,以及占用的字节数,用于判断是什么类占用了大量内存。
3、 另一类是jstack为主的线程分析技术,主要通过观察runnable、block、waiting三种线程状态,找到性能瓶颈。其中重点关注BLOCKED状态的数量,其次是WAITING状态的数量。
4、 Jstack还有一个需要我们要注意的就是lock的数量,其中<>的括号中标注的是锁号,如果出现大量的线程显示waiting lock<>且锁号相同,那么性能瓶颈点很可能就在这里。
5、 最特殊的是runnable如果出现大量线程停留在一个我们非常常见的方法时,也是也要提高警惕,很有可能这个类或者方法是非线程安全的,或者是由于递归或者特殊算法导致的无法退出。
5.2.2.3 数据库慢SQL
数据库慢SQL可以通过以下几个办法发现:
1、如果是oracle数据库,可以找dba要AWR日志,如果是其他数据库可以问问dba有没有相关日志,这种方法可以查找到一个时间段范围内慢SQL排名前20的执行次数、SQL内容、平均耗时时间。
2、可以通过数据库session,查询session执行时间长的,通过session号找到所对应的SQL语句。这个方法能找到此时慢的SQL语句是什么。
3、如果使用了Druid连接器,可以使用它的监控界面查看慢SQL情况
5.2.2.4 数据库执行计划
目前大部分关系型数据库都可以查询sql语句的执行计划,只需在sql语句前面加上Explain for命令 以oracle为例:
Explain for select * from dual;
但是各种关系型数据库的执行计划的前缀和展示的结果略不相同,一下图片是一个的典型oracle执行计划输出结果

对于一些特殊的客户端可能显示是这样的

但是不论是哪一种,我们都可以看到几个重要信息,参与sql的表的信息,是否走了索引,是否是全表扫描。这已经足够我们分析sql慢的原因了。这个技术对于SQL优化非常有帮助。如果实在看不懂也可以找DBA进行帮助。

你可能感兴趣的:(项目BUG)