type
status
date
slug
summary
tags
category
icon
password
 

问题背景

参与的光伏项目需要提供搬运任务的数据导出功能,便于客户对产能数据进行统计与跟踪,因此我们在系统的任务看板页面增加了根据执行时间导出任务流水的功能。

问题场景

某天现场反馈系统页面无法登录,非常影响系统监控,需要紧急支持恢复现场。确认现场在系统出现故障之前有什么操作,了解到当天换班时现场执行了数据导出操作,由于导出失败换了几个终端操作都没有响应后就出现了这个故障场景。

问题分析

确认了问题触发的操作后拿到业务日志结合GC日志分析,发现了内存溢出的报错日志,结合故障发生前的操作查看导出模块的实现,发现该功能并没有限制任务导出的时间间隔,当用户导出时间范围较大时,该功能会直接查询数据库中指定时间间隔的所有数据,导致数据库的查询压力增加,且JVM由于内存无法被回收释放最终抛出GC异常。
经过问题分析之后再次通过在测试环境通过灌入数据执行相同操作,复现场景后确认了问题发生的原因,随后紧急让前端隐藏该功能操作,现场紧急更新一版前端避免该操作再次引起问题。

问题修复

该问题的修复我们实际上是逐步迭代的,版本的迭代大致分为如下步骤:
  1. 功能禁用:首先在问题修复阶段我们能做的就是有损服务质量,由前端隐藏功能按钮,优先保障其他正向业务功能;
  1. 启用功能客户端限制:由前端在导出操作时限制最多只能导出七天的数据,初步满足客户的部分需求;同时后端对查询数据的表增加索引,确保数据查询速度;
  1. 文件缓存操作:客户端发起导出请求后可以立即返回响应,后端导出线程负责将文件写入文件服务器中;页面增加文件下载页面,这样对于相同请求参数的导出请求,可以避免后端重复查询。

问题复盘

随后内部组织进行故障场景复盘,分析这个问题场景今后如何避免,同时怎么做能够加快问题的定位速度
  • 导出操作的设计不够合理
  • 后端数据查询时没有合理设计,包括数据查询没有利用数据库索引,数据查询操作没有合理评估数据分批查询
  • JVM未设置内存溢出自动dump快照,导致后续分析只能根据异常日志以及测试环境复现来确定根因
  • 历史数据的归档与定期清理:对于数据增长频率较快的业务场景,还需要向客户确认数据需要保留的最大时长,后台设计历史数据定时清理功能
 
针对导出操作的不合理设计,我从后端角度提出了几点自己的见解
  • 对于时间跨度较大的流水数据,建议从产品侧要求用户分批次导出,这样能够从根本上减少单次导出请求的服务端压力;
  • 文件缓存:对于数据导出操作后,不同步返回导出操作的结果,而是后台异步处理导出请求,将数据写入到文件服务器中,这样设计对于不同客户端发起的相同时间段的导出请求能够进行合并,有效减少服务端的IO压力;
  • 操作限制:对于单个客户端的导出操作需要做限制,避免出现单个客户端导出操作过多造成服务压力;同时服务端需要对单个客户端的相同导出请求做幂等限制;
 
 
对于操作限制的思考是因为考虑到异常场景的发生我认为都是具备级联特性的,故障的发生可能导致客户端的不断重试,最简单的例子,网络卡顿或者服务端处理慢很可能导致客户端的重复点击提交,这就可能进一步增加系统处理的压力,任务流水导出功能由于前期设计没有考虑到数据量与导出功能的良好设计,导致数据量增大之后客户点击导出操作长时间没有响应,客户认为是自己网络的问题,因此客户重新刷新页面再次提交,发现还是不能导出后,再次推测可能是自己客户端的问题,于是找其他同事重试这个操作,最终导致服务端不断大批量查询任务数据,导致内存溢出。
 
 
该故障为什么没有在测试阶段暴露出来,是因为该项目属于新业务领域的前期尝试阶段,所以测试也只是经过了基本的功能测试,而没有基于实际的运行来合理预估可能的数据压力进行压力测试。
 
 
思考
  • 历史数据的定时归档(基于客户决定是否定时删除)
  • 导出操作的功能设计
  • JVM的监控与日志打印,包括内存溢出的自动dump
  • 单个功能故障引发的级联崩溃场景
  • 线性数据增长功能的压力测试
 
数据导出思考我对软件架构高可用的思考
Loading...