这一年遇上的线上故障
特别说明:封面图片来源 https://www.zcool.com.cn/work/ZMjc0MTc4MTI=.html PS:未授权…若作者看到后不愿意授权可联系下架
做技术最怕什么?也许就是出现线上故障吧,因为它不仅仅和你的绩效有关,还体现着你的能力。
项目上线后,随着系统运行时间的增长,一瞬间用户访问量提升,用户某个奇怪行为都可能给系统带来致命的问题,本文总结了这一年来遇到的线上问题以及解决思路。
接口
订单查询
在公司负责支付方面,离不开订单系统,订单系统提供了订单查询RPC接口,某天早上突然收到运营同学反馈,某业务系统服务首页一直提示报错(进入页面首页后会轮训查询当前用户未支付的订单),第一时间去查看日志,发现接口响应时长都接近10秒,同时找运维同学查看数据库状态,此时数据库的CPU已经达到了90%+(数据库CPU没做报警策略,没能第一时间知道),当时数据库的配置是4C16G,理论上不会轻易搞崩。
查看订单系统的日志后第一时间查看了CPU运行信息和内存使用情况,CPU和内存使用情况都正常,问题说明出现在了数据库。一台服务器CPU爆表说明有大量任务在执行或者出现了严重的资源竞争,比如死锁。数据库服务只部署了数据库,没有其他服务,那么问题可能是出现了资源竞争。
通过查看数据库当时的运行情况,出现几条严重的慢SQL,同时提示了行锁过多。由于订单服务是我后面接手的,前期的表创建我都没动过(说明一个问题,在接手一个服务后,除了了解业务流程外还要第一时间去了解数据库,比如索引!),当时的数据量达到了数十万,核心的订单表只有一个主键索引,导致每次查询(通过其他字段)都是全表扫描,行锁升级为表锁,导致数据库锁竞争情况严重,当时的解决方案是对查询频繁的字段加了索引(比如用户ID),通过索引这一优化很好的解决了该问题。
普通查询
年后上班接到一个需求,在他人负责的系统中写两个功能,信息录入和文件导出,当时提供了几个接口,一是通过身份证查询信息,确认当前用户是否录入过信息,一个是文件导出。前期上线后系统运行一切正常,在某天突然有人反馈不能保存数据了,通过身份证查询信息也十分的慢。第一反应是网络问题吗?访问服务页面偶尔响应慢,接口也是如此,开始怀疑网络问题,是否是专网的网络太慢,导致Nginx转发时出现大量等待的情况呢?于是带着怀疑去服务器检查网络情况,发现问题并非如此,在ping的情况下,没有丢包,响应时间也是正常,那么问题还可能会出现在哪呢?页面访问速度异常,接口也是异常,页面不会操作数据库资源,接口会查询数据,出问题的地方就只剩下两种情况了,一是服务器出问题,二是数据库出问题。由于页面访问也出现异常,可以先排除数据库问题。
既然是服务器问题,首先应该检查CPU负载,内存情况。通过top命令查看当时CPU负载情况,数值都属于正常范围,同时查看内存分配也处于正常情况。面对这样的情况一脸懵逼,回到问题,一共提供了几个接口,出现问题的时间基本发生在同一时间段,大量占用资源的操作就是导出数据,那么会不会和数据库连接有关?带着这个疑问去查看了项目的数据库配置,没有超时时间控制,最大连接数8。如果数据库连接没有超时时间控制,那么是不是意味着获取连接的线程会一直阻塞?为了方便排查具体问题,在服务器上安装了阿里开源的arthas,通过线程分析,发现大量的HTTP线程处于阻塞状态,而发生阻塞的代码就是获取数据库连接,问题定位到,修改了项目的数据库连接数和超时时间,问题解决。
当然,针对文件导出这样的需求,一般的做法是凌晨某个时间段生成文件,提供下载功能即可,要不然某个时间段大量的文件导出会大量消耗数据库资源,比如支付宝和微信的账单下载,他们就会提前生成文件。
数据库的配置和服务器配置一样,如果我们配置不合理,就会出现各种资源耗尽。面对多样的配置,一定要充满好奇心,去看看作用是啥,怎样合理使用会更好,这样才能尽可能的减少项目故障。
磁盘
服务器的日志你会保留多少天?做支付时日志我会选择保留180天,半年的时间,确保半年内出现支付问题能追溯。在接触的一些项目中,有一些没有做到很好的日志管理,导致磁盘刷爆。当时接手一个项目,其中也有文件导出功能,当时遇到的情况就是文件导出不了(使用的阿里开源的easy excel),通过排查系统日志并未发现系统错误或程序上的异常,问题出现在哪呢?
文件导出功能,它的执行过程是怎样的呢?首先是数据从数据库加载到内存,那么接下来就是从内存写文件了。把内存的数据写入文件有两种做法,一是先在内存中生成文件写入数据然后落磁盘,二是文件先落磁盘,然后从内存中输出到磁盘文件。两种方式各有利弊,大多数会选择后者,前者容易出现OOM,不太现实。
再排查了系统和程序问题后,偶然下执行了df -h 命令,发现磁盘没空间了,在确认了日志是否可删的前提下删除了部分日志,然后在执行文件导出功能,发现功能恢复正常。当时想,为什么文件导出与磁盘还有一定关系呢?通过查看easy excel写入文件的逻辑是有默认把文件写入磁盘的,如果你磁盘写不了,文件自然就无法导出了,具体源码代码段忘记了,遇到问题还是需要去看源码,那里是你找到答案最近的地方。
论,服务器运维的重要性。
内存
说到内存,内存的问题大致分两种,内存溢出和内存泄漏。
内存溢出:程序没有足够的内存使用时,就会发生内存溢出。内存溢出后程序基本上就无法正常运行了。
内存泄漏:当程序不能及时释放内存,导致占用内存逐渐增加,就是内存泄漏。
内存泄漏一般不会导致程序无法运行。不过持续的内存泄漏,累积到内存上限时,就会发生内存溢出。Java服务出现OOM,最常见的原因一般有以下三点:
内存确实分配过小,内存确实不够用;
某一个对象被频繁申请,却没有释放,内存不断泄漏,导致内存耗尽;
某一个资源被频繁申请,系统资源耗尽,例如:不断创建线程,不断发起网络连接;
当时的情况是,某服务存在大量的数据上传工作,即从数据库中读取数据,通过程序上传到ODPS。某天服务器报警说内存不够了,登上服务器查看了内存大小,4G内存,但并不代表JVM可用内存就是4G,通过jmap(jmap -heap pid)命令确认了JVM的内存分配情况,通过该命令可以查看新生代,老生代堆内存的分配大小以及使用情况。
内存分配正常,是不是存在对象被频繁申请呢?毕竟上传数据离不开数据查询,是否可能是一次性查询的数据过多,导致OOM,通过命令(jmap -histo:live pid | more)查看最耗内存的对象,果然,某个DO存在大量实例对象,由于都是强引用,JVM在垃圾回收时没有回收这些对象,导致出现OOM。
当时的解决方案是加大内存,从4G增加到8G,问题得等解决。当然这并不意味着这是最好的解决方案。后面我们对程序进行了优化,查询的数据是List
总结
- 有问题不可怕,我们需要自信,从容的面对他们,这样才能淡定的去解决问题。
- 配置合理化,使用框架或工具一定要充满好奇,去看看配置有哪些,作用是什么。
- 接手项目后,除了了解业务玩法还要重点关注数据库。