许多人在面试时,会被问到这样的问题:遇到过什么系统故障?怎么解决的?下面是笔者凭据自己15年互联网研发履历总结的多个线上故障真实案例。相信可以帮你从容应劈面试官的提问!本文图不多,但内容很干!明白为主,面试为辅,学以致用!故障一:JVM频繁FULL GC快速排查在分享此案例前,先聊聊哪些场景会导致频繁Full GC:内存泄漏(代码有问题,工具引用没实时释放,导致工具不能实时接纳)死循环大工具尤其是大工具,80%以上的情况就是他。
那么大工具从那里来的呢?数据库(包罗Mysql和Mongodb等NOSql数据库),效果集太大第三方接口传输的大工具消息行列,消息太大凭据多年一线互联网履历,绝大部门情况是数据库大效果集导致。好,现在我们开始先容这次线上故障:在没有任何公布的情况下,POP服务(接入第三方商家的服务)突然开始疯狂Full GC,视察堆内存监控没内存泄漏,回滚到前一版本,问题仍然存在,尴尬了!!!根据通例做法,一般先用jmap导出堆内存快照(jmap -dump:format=b,file=文件名 [pid]),然后用mat等工具分析出什么工具占用了大量空间,再检察相关引用找到问题代码。这种方式定位问题周期会比力长,如果是关键服务,长时间不能定位解决问题,影响太大。
下面来看看我们的做法。先根据通例做法分析堆内存快照,与此同时另外的同学去检察数据库服务器网络IO监控,如果数据库服务器网络IO有显着上升,而且时间点吻合,基本可以确定是数据库大效果集导致了Full GC,赶快找DBA快速定位大SQL(对DBA来说很简朴,分分钟搞定,如果DBA不知道怎么定位,那他要被开除了,哈哈),定位到SQL后再定位代码就很是简朴了。
根据这种措施,我们很快定位了问题。原来是一个接口必传的参数没传进来,也没加校验,导致SQL语句where后面少了两个条件,一次查几万条记载出来,真坑啊!这种方法是不是要快许多,哈哈,5分钟搞定。
其时的DAO层是基于Mybatis实现的,出问题的SQL语句如下:<select id="selectOrders" resultType="com.***.Order" >select * from user where 1=1<if test=" orderID != null ">and order_id = #{orderID}</if ><if test="userID !=null">and user_id=#{userID}</if ><if test="startTime !=null">and create_time >= #{createTime}</if ><if test="endTime !=null">and create_time <= #{userID}</if ></select>上面SQL语句意思是凭据orderID查一个订单,或者凭据userID查一个用户所有的订单,两个参数至少要传一个。可是两个参数都没传,只传了startTime和endTime。所以一次Select就查出了几万条记载。
所以我们在使用Mybatis的时候一定要慎用if test,一不小心就会带来灾难。厥后我们将上面的SQL拆成了两个:凭据订单ID查询订单:<select id="selectOrderByID" resultType="com.***.Order" >select * from user whereorder_id = #{orderID}</select>凭据userID查询订单:<select id="selectOrdersByUserID" resultType="com.***.Order" >select * from user whereuser_id=#{userID}<if test="startTime !=null">and create_time >= #{createTime}</if ><if test="endTime !=null">and create_time <= #{userID}</if ></select>故障二:内存泄漏先容案例前,先相识一下内存泄漏和内存溢出的区别。内存溢出:法式没有足够的内存使用时,就会发生内存溢出。内存溢出后法式基本上就无法正常运行了。
内存泄漏:当法式不能实时释放内存,导致占用内存逐渐增加,就是内存泄漏。内存泄漏一般不会导致法式无法运行。不外连续的内存泄漏,累积到内存上限时,就会发生内存溢出。
在Java中,如果发生内存泄漏,会导致GC接纳不彻底,每次GC后,堆内存使用率逐渐增高。下图是JVM发生内存泄漏的监控图,我们可以看到每次GC后堆内存使用率都比以前提高了。其时内存泄漏的场景是,用当地缓存(公司基础架构组自己研发的框架)存放了商品数据,商品数量不算太多,几十万的样子。
如果只存热点商品,内存占用不会太大,可是如果存放全量商品,内存就不够了。初期我们给每个缓存记载都加了7天的逾期时间,这样就可以保证缓存中绝大部门都是热点商品。
不外厥后当地缓存框架经由一次重构,逾期时间被去掉了。没有了逾期时间,日积月累当地缓存越来越大,许多冷数据也被加载到了缓存。直到有一天接到告警短信,提示堆内存过高。赶快通过jmap(jmap -dump:format=b,file=文件名 [pid] )下载了堆内存快照,然后用eclipse的mat工具分析快照,发现了当地缓存中有大量的商品记载。
定位问题后赶快让架构组加上了逾期时间,然后逐个节点重启了服务。亏了我们加了服务器内存和JVM堆内存监控,实时发现了内存泄漏的问题。否则随着泄漏问题日积月累,如果哪天真的OOM就惨了。所以技术团队除了做好CPU,内存等运维监控,JVM监控也很是重要。
故障三:幂等问题许多年前,笔者在一家大型电商公司做Java法式员,其时开发了积分服务。其时的业务逻辑是,用户订单完结后,订单系统发送消息到消息行列,积分服务接到消息后给用户积分,在用户现有的积分上加上新发生的积分。由于网络等原因会有消息重复发送的情况,这样也就导致了消息的重复消费。
其时笔者还是个初入职场的小菜鸟,并没有思量到这种情况。所以上线后偶然会泛起重复积分的情况,也就是一个订单完结后会给用户加两次或多次积分。厥后我们加了一个积分记载表,每次消费消息给用户增加积分前,先凭据订单号查一遍积分记载表,如果没有积分记载才给用户增加积分。
这也就是所谓的“幂等性”,即多次重复操作不影响最终的效果。实际开发中许多需要重试或重复消费的场景都要实现幂等,以保证效果的正确性。例如,为了制止重复支付,支付接口也要实现幂等。故障四:缓存雪崩我们经常会遇到需要初始化缓存的情况。
好比,我们曾经履历过用户系统重构,用户系统表结构发生了变化,缓存信息也要变。重构完成后上线前,需要初始化缓存,将用户信息批量存入Reids。每条用户信息缓存记载逾期时间是1天,记载逾期后再从数据库查询最新的数据并拉取到Redis中。
灰度上线时一切正常,所以很快就全量公布了。整个上线历程很是顺利,码农们也很开心。
不外,第二天,灾难发生了!到某一个时间点,种种报警络绎不绝。用户系统响应突然变得很是慢,甚至一度没有任何响应。检察监控,用户服务CPU突然飙高(IO wait很高),Mysql会见量激增,Mysql服务器压力也随之暴增,Reids缓存掷中率也跌到了极点。依赖于我们强大的监控系统(运维监控,数据库监控,APM全链路性能监控),很快定位了问题。
原因就是Reids中大量用户记载集中失效,获取用户信息的请求在Redis中查不到用户记载,导致大量的请求穿透到数据库,瞬间给数据库带来庞大压力。同时用户服务和相关联的其他服务也都受到了影响。
这种缓存集中失效,导致大量请求同时穿透到数据库的情况,就是所谓的“缓存雪崩”。如果没到缓存失效时间点,性能测试也测不出问题。
所以一定要引起大家注意。所以,需要初始化缓存数据时,一定要保证每个缓存记载逾期时间的离散性。例如,我们给这些用户信息设置逾期时间,可以接纳一个较大的牢固值加上一个较小的随机值。
好比逾期时间可以是:24小时 + 0到3600秒的随机值。故障五:磁盘IO导致线程阻塞问题发生在2017年下半年,有一段时间地理网格服务时不常的会响应变慢,每次连续几秒钟到几十秒钟就自动恢复。如果响应变慢是连续的还好办,直接用jstack抓线程客栈,基本可以很快定位问题。
关键连续时间只有最多几十秒钟,而且是偶发的,一天只发生一两次,有时几天才发生一次,发生时间点也不确定,人盯着然后用jstack手工抓线程客栈显然不现实。好吧,既然手工的措施不现实,咱们就来自动的,写一个shell剧本自动定时执行jstack,5秒执行一次jstack,每次执行效果放到差别日志文件中,只生存20000个日志文件。Shell剧本如下:#!/bin/bashnum=0log="/tmp/jstack_thread_log/thread_info"cd /tmpif [ ! -d "jstack_thread_log" ]; then mkdir jstack_thread_logfiwhile ((num <= 10000)); do ID=`ps -ef | grep java | grep gaea | grep -v "grep" | awk '{print $2}'` if [ -n "$ID" ]; then jstack $ID >> ${log} fi num=$(( $num + 1 )) mod=$(( $num%100 )) if [ $mod -eq 0 ]; then back=$log$num mv $log $back fi sleep 5done下一次响应变慢的时候,我们找到对应时间点的jstack日志文件,发现内里有许多线程阻塞在logback输出日志的历程,厥后我们精简了log,而且把log输出改成异步,问题解决了,这个剧本果真好用!建议大家保留,以后遇到类似问题时,可以拿来用!故障六:数据库死锁问题在分析案例之前,我们先相识一下MySQL INNODB。在MySQL INNODB引擎中主键是接纳聚簇索引的形式,即在B树的叶子节点中既存储了索引值也存储了数据记载,即数据记载和主键索引是存在一起的。
而普通索引的叶子节点存储的只是主键索引的值,一次查询找到普通索引的叶子节点后,还要凭据叶子节点中的主键索引去找到聚簇索引叶子节点并拿到其中的详细数据记载,这个历程也叫“回表”。故障发生的场景是关于我们商城的订单系统。
有一个定时任务,每一小时跑一次,每次把所有一小时前未支付订单取消掉。而客服后台也可以批量取消订单。
订单表t_order结构大致如下:id是表的主键,created_time字段上是普通索引。聚簇索引(主键id)普通索引(created_time字段)定时任务每一小时跑一次,每次把所有一小时前两小时内的未支付订单取消掉,好比上午11点会取消8点到10点的未支付订单。
SQL语句如下:update t_order set status = 'CANCELLED' where created_time > '2020-01-01 08:00:00' and created_time < '2020-01-01 10:00:00' and status = 'UNPAID'客服批量取消订单SQL如下:update t_order set status = 'CANCELLED' where id in (2, 3, 5) and status = 'UNPAID'上面的两条语句同时执行就可能发生死锁。我们来分析一下原因。第一条定时任务的SQL,会先找到created_time普通索引并加锁,然后再再找到主键索引并加锁。
第一步,created_time普通索引加锁 第二步,主键索引加锁第二条客服批量取消订单SQL,直接走主键索引,直接在主键索引上加锁。我们可以看到,定时任务SQL对主键加锁顺序是5,4,3,2。
客服批量取消订单SQL对主键加锁顺序是2,3,5。当第一个SQL对3加锁后,正准备对2加锁时,发现2已经被第二个SQL加锁了,所以第一个SQL要等候2的锁释放。而此时第二个SQL准备对3加锁,却发现3已经被第一个SQL加锁了,就要等候3的锁释放。
两个SQL相互等候对方的锁,也就发生了“死锁”。解决措施就是从SQL语句上保证加锁顺序一致。或者把客服批量取消订单SQL改成每次SQL操作只能取消一个订单,然后在法式里多次循环执行SQL,如果批量操作的订单数量不多,这种笨措施也是可行的。
故障七:域名挟制先看看DNS剖析是怎么回事,当我们会见www.baidu.com时,首先会凭据www.baidu.com到DNS域名剖析服务器去查询百度服务器对应的IP地址,然后再通过http协议会见该IP地址对应的网站。而DNS挟制是互联网攻击的一种方式,通过攻击域名剖析服务器(DNS)或者伪造域名剖析服务器,把目的网站域名剖析到其他的IP。从而导致请求无法会见目的网站或者跳转到其他网站。如下图:下面这张图是我们曾经履历过的DNS挟制的案例。
看图中的红框部门,原来上方的图片应该是商品图片,可是却显示成了广告图片。是不是图片配错了?不是,是域名(DNS)被挟制了。原本应该显示存储在CDN上的商品图片,可是被挟制之后却显示了其他网站的广告链接图片。由于其时的CDN图片链接接纳了不宁静的http协议,所以很容易被挟制。
厥后改成了https,问题就解决了。固然域名挟制有许多方式,https也不能规避所有问题。所以,除了一些宁静防护措施,许多公司都有自己的备用域名,一旦发生域名挟制可以随时切换到备用域名。
故障八:带宽资源耗尽带宽资源耗尽导致系统无法会见的情况,虽然不多见,可是也应该引起大家的注意。来看看,之前遇到的一起事故。场景是这样的。
社交电商每个分享出去的商品图片都有一个唯一的二维码,用来区分商品和分享者。所以二维码要用法式生成,最初我们在服务端用Java生成二维码。前期由于系统会见量不大,系统一直没什么问题。可是有一天运营突然搞了一次优惠力度空前的大促,系统瞬时会见量翻了几十倍。
问题也就随之而来了,网络带宽直接被打满,由于带宽资源被耗尽,导致许多页面请求响应很慢甚至没任何响应。原因就是二维码生成数量瞬间也翻了几十倍,每个二维码都是一张图片,对带宽带来了庞大压力。怎么解决呢?如果服务端处置惩罚不了,就思量一下客户端。
把生成二维码放到客户端APP处置惩罚,充实使用用户终端手机,现在Andriod,IOS或者React都有相关生成二维码的SDK。这样不光解决了带宽问题,而且也释放了服务端生成二维码时消耗的CPU资源(生成二维码历程需要一定的盘算量,CPU消耗比力显着)。
本文来源:爱游戏app官方下载-www.yz-gf.com