先说明一点,本文所说思路和方案基于纯Java实现,不依赖任何大数据技术
业务系统开发中,产品经常提出这样的功能,要求系统系统支持excel格式数据导出,这种功能再常见不过,熟练的程序员可能几个小时就搞定了,然后随着数据量的增加,使用频率的提高,有没有遇到一下情况呢 ?
- web端突然cpu,内存突然使用飙高,然后访问502
- 后台服务突然压力过大,影响核心业务功能使用
- 数据库慢SQL突然增加,数据库服务器压力过大
- 数据导出越来越慢,经常挂掉,单excel文件越来越大
那么这些问题改如何解决呢?提几个问题,大家可以先思考下
- 数据导出功能是核心功能吗?
- 数据导出是否需要和核心链路解耦?
- 数据导出是否必须实时数据呢?
说一下笔者之前所在业务部门的背景,曾有过一段计费系统开发经历,涉及客户数量在百万级,每个客户每月的账单明细是需要供内部业务人员和外部客户人员查询或者导出,这个账单明细量级在数千到上千万不等,这是无论是使用频率和导出数量都是需要解决的问题,下面具体谈谈我们的方案演进
最早的方案,web输入条件,点导出,后台循环查询全部落入内存,然后往excel文件写,限制一次导出2万条,碰到大数据的开始找DBA,提流程。后来慢慢的数据安全要求越来越严,审批流程越来越长,研发每天大量时间花在找人,提流程上,无意义的事,所以必须想办法。
方案选型:
为什么选第二种呢?说一下理由:
- 当时团队内部数据所有数据还没有接入大数据平台
- 公司大数据平台操作需要学习,考试,通过之后才能使用(比较麻烦,团队内部人少)
- 和大数据平台打通需要沟通,协调等,周期比较长,要不然也只能基于大数据平台写脚本,依然需要研发人员介入,比较麻烦,效率低下
- 大数据的那一套团队缺乏有实战经验的人
接下来说一下我们需要达成的目标
- 全程导出无人工干预,业务想怎么导,就怎么导
- 效率,千万级数据在小时内完成。
- 0影响业务系统本身的功能(包括web,后台服务,数据库服务)
- 支持业务实时查询导出进度监控
- 支持任务失败重试,失败报警
账单明细数据涉及的数据范围主要是核心业务数据(比如:订单,采购单等这种需要计费的数据),还有一部分非核心数据,比如:商家信息,四级地址信息,仓库信息,配置信息,这两种数据有如下特点:
- 前者数据准确性比较高,不允许出错,因为涉及账单信息
- 后者对准确性要求不高,而且商家信息,地址信息,仓库信息等这一类变化频率低
业务人员每月月初会导出上月商家账单明细数据,提取核心字段(商家,月份)
基于以上这些特性,我们架构设计是这样的
- 所有数据全部查询走从库,分布式导出集群统一连接业务数据库的从库
- 业务人员从web页面添加一条导出任务,经任务拆分规则引擎执行后,落入多条主子任务,比如业务人员导出A,B商家2月份的任务,拆分引擎识别之后可以A,B * 28 = 54条任务(2月份28天),
- 分布式导出集群,定时从数据库同步非核心数据(商家,仓库等),落入本地磁盘,redis,内存等多级缓存中(这里每台机器存全量缓存,redis全局缓存存一份)。
- 导出集群定时抓取未执行的主任务,抓取之后,锁定任务,防止其它机器执行,然后抓取父任务对应的所有子任务,
- 组装任务执行上下文,交给线程池执行
- 执行过程中,定时汇报执行进度,写入缓存
- 每条子任务执行失败,自动重试,最大3次
- 当最后一条子任务执行完成后,对所有文件压缩存储,上传云存储,发送连接给客户
- 最后清理临时文件
最后总结一下,一切设计都要围绕业务诉求和现有的问题点,比如频繁的导出影响数据库,影响服务,我们就要考虑是否剥离,走从库可以不?频繁的跨多库查询,就要考虑是否吧一些非核心不长变的数据缓存。海量数据导出就要是否可以分批次,多机并行执行,这一切都要结合业务特性来考虑。