系统干崩了,只认代码不认人

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 为了保障系统的高可用和稳定,我发誓以后只认代码不认人。文末总结了几个小教训,希望对你有帮助。

各位朋友听我一句劝,写代码提供方法给别人调用时,不管是内部系统调用,还是外部系统调用,还是被动触发调用(比如MQ消费、回调执行等),一定要加上必要的条件校验。千万别信某些同事说的这个条件肯定会传、肯定有值、肯定不为空等等。这不,临过年了我就被坑了一波,弄了个生产事故,年终奖基本是凉了半截。

为了保障系统的高可用和稳定,我发誓以后只认代码不认人。文末总结了几个小教训,希望对你有帮助。

一、事发经过

我的业务场景是:业务A有改动时,发送MQ,然后应用自身接受到MQ后,再组合一些数据写入到Elasticsearch。以下是事发经过:

  1. 收到一个业务A的异常告警,当时的告警如下:

  2. 咋一看觉得有点奇怪,怎么会是Redis异常呢?然后自己连了下Redis没有问题,又看了下Redis集群,一切正常。所以就放过了,以为是偶然出现的网络问题。
  3. 然后技术问题群里 客服 反馈有部分用户使用异常,我警觉性的感觉到是系统出问题了。赶紧打开了系统,确实有偶发性的问题。
  4. 于是我习惯性的看了几个核心部件:
    1. 网关情况、核心业务Pod的负载情况、用户中心Pod的负载情况。
    2. Mysql的情况:内存、CPU、慢SQL、死锁、连接数等。
  5. 果然发现了慢SQL和元数据锁时间过长的情况。找到了一张大表的全表查询,数据太大,执行太慢,从而导致元数据锁持续时间太长,最终数据库连接数快被耗尽。
SELECT xxx,xxx,xxx,xxx FROM 一张大表
  1. 立马Kill掉几个慢会话之后,发现系统仍然没有完全恢复,为啥呢?现在数据库已经正常了,怎么还没完全恢复呢?又继续看了应用监控,发现用户中心的10个Pod里有2个Pod异常了,CPU和内存都爆了。难怪使用时出现偶发性的异常呢。于是赶紧重启Pod,先把应用恢复。
  2. 问题找到了,接下来就继续排查为什么用户中心的Pod挂掉了。从以下几个怀疑点开始分析:
    1. 同步数据到Elasticsearch的代码是不是有问题,怎么会出现连不上Redis的情况呢?
    2. 会不会是异常过多,导致发送异常告警消息的线程池队列满了,然后就OOM?
    3. 哪里会对那张业务A的大表做不带条件的全表查询呢?
  3. 继续排查怀疑点a,刚开始以为:是拿不到Redis链接,导致异常进到了线程池队列,然后队列撑爆,导致OOM了。按照这个设想,修改了代码,升级,继续观察,依旧出现同样的慢SQL 和 用户中心被干爆的情况。因为没有异常了,所以怀疑点b也可以被排除了。
  4. 此时基本可以肯定是怀疑点c了,是哪里调用了业务A的大表的全表查询,然后导致用户中心的内存过大,JVM来不及回收,然后直接干爆了CPU。同时也是因为全表数据太大,导致查询时的元数据锁时间过长造成了连接不能够及时释放,最终几乎被耗尽。
  5. 于是修改了查询业务A的大表必要校验条件,重新部署上线观察。最终定位出了问题。

二、问题的原因

因为在变更业务B表时,需要发送MQ消息( 同步业务A表的数据到ES),接受到MQ消息后,查询业务A表相关连的数据,然后同步数据到Elasticsearch。

但是变更业务B表时,没有传业务A表需要的必要条件,同时我也没有校验必要条件,从而导致了对业务A的大表的全表扫描。因为:

某些同事说,“这个条件肯定会传、肯定有值、肯定不为空...”,结果我真信了他!!!

由于业务B表当时变更频繁,发出和消费的MQ消息较多,触发了更多的业务A的大表全表扫描,进而导致了更多的Mysql元数据锁时间过长,最终连接数消耗过多。

同时每次都是把业务A的大表查询的结果返回到用户中心的内存中,从而触发了JVM垃圾回收,但是又回收不了,最终内存和CPU都被干爆了。

至于Redis拿不到连接的异常也只是个烟雾弹,因为发送和消费的MQ事件太多,瞬时间有少部分线程确实拿不到Redis连接。

最终我在消费MQ事件处的代码里增加了条件校验,同时也在查询业务A表处也增加了的必要条件校验,重新部署上线,问题解决。

三、总结教训

经过此事,我也总结了一些教训,与君共勉:

  1. 时刻警惕线上问题,一旦出现问题,千万不能放过,赶紧排查。不要再去怀疑网络抖动问题,大部分的问题,都跟网络无关。
  2. 业务大表自身要做好保护意识,查询处一定要增加必须条件校验。
  3. 消费MQ消息时,一定要做必要条件校验,不要相信任何信息来源。
  4. 千万别信某些同事说,“这个条件肯定会传、肯定有值、肯定不为空”等等。为了保障系统的高可用和稳定,咱们只认代码不认人
  5. 一般出现问题时的排查顺序:
    1. 数据库的CPU、死锁、慢SQL。
    2. 应用的网关和核心部件的CPU、内存、日志。
  6. 业务的可观测性和告警必不可少,而且必须要全面,这样才能更快的发现问题和解决问题。

======>>>>>> 关于我 <<<<<<======

本篇完结!欢迎点赞 关注 收藏!!!

原文链接https://mp.weixin.qq.com/s/TvIpTZq0XO8v9ccYSsM37Q

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
小程序 数据可视化 前端开发
编写小程序用什么软件
编写小程序用什么软件
61 5
|
3月前
|
算法 程序员
程序代码设计步骤
程序的设计过程,并不是立刻就进行代码设计,一般来讲包括设置文件的存放位置、说明书的设计、代码设计、程序测试、程序调试、注释说明。
87 7
|
4月前
|
搜索推荐 API 数据处理
什么是无代码?哪些人适合通过无代码来开发自己的业务系统
无代码是一种无需编程知识即可构建应用的方法。用户通过拖拽组件并设置参数,即可搭建功能完备的应用系统。其核心特点是普适性和包容性,降低了技术门槛,提供了直观界面,能快速响应需求变化,同时降低成本并具有一定的可扩展性。无代码适合一线业务人员、中小企业及专业技术人员使用,但在高度定制化、复杂逻辑处理或深度系统集成方面仍需传统开发。以草料二维码为例,无代码平台提供活码、表单、计划管理等功能,助力快速搭建各类应用系统,使每个人都能成为开发者。
|
3月前
|
Java 调度
代码打造每日任务系统
在游戏开发中,每日任务系统对提升玩家活跃度和留存率至关重要。通过Java的面向对象特性,可将每日任务抽象为`Task`类,并通过实例化及方法调用实现任务创建、执行与奖励功能。进一步,可以创建`DailyTaskSystem`类来管理所有每日任务,包括添加、删除和获取任务列表等操作。这种设计不仅简化了任务管理,还增强了游戏的可玩性和吸引力。更多细节和实现方法可见相关游戏逻辑设计与具体需求。
43 0
|
7月前
|
存储 开发工具 数据库
认识HIS系统 HIS系统的主要功能解释说明
HIS系统即医院信息系统(全称为Hospital information System) ,是指利用计算机软硬件技术和网络通信技术等现代化手段,对医院及其所属各部门的人流、物流、财流进行综合管理,对在医疗活动各阶段产生的数据进行采集、存储、处理、提取、传输、汇总,加工形成各种信息,从而为医院的整体运行提供全面的自动化管理及各种服务的信息系统。
557 5
|
消息中间件 JavaScript 小程序
这些老系统代码,是猪写的么?
这些老系统代码,是猪写的么?
|
存储 小程序 容器
小程序中实现文章的关注功能
小程序中实现文章的关注功能
小程序中实现文章的关注功能
|
存储 JSON 前端开发
接口测试平台代码实现52: 自动异常测试-5
打开我们上次没写完的P_apis.html 找到这个error_play函数,我们已经替换好了请求体,那么接下来就把新请求体和接口id传递给后台即可
接口测试平台代码实现52: 自动异常测试-5
|
JSON JavaScript 测试技术
接口测试平台代码实现51: 自动异常测试-4
接口测试平台代码实现51: 自动异常测试-4
接口测试平台代码实现51: 自动异常测试-4
|
XML JSON JavaScript
接口测试平台代码实现50: 自动异常测试-3
本节开始之前,先解决下关于调试层遗漏的一个设计缺陷。就是当已经打开了一个接口的调试层后,如果直接点击其他接口的调试层便会出现显示错误。这类的问题非常多,我们最好是直接根本上绕过去。
接口测试平台代码实现50: 自动异常测试-3
下一篇
DataWorks