背景
日常开发中我们经常会遇到数据导出到excel的需求,由于导出的数据量大往往会出现内存溢出或者超时等问题。往往的做法也是从业务上去规避的,比如:
- 限制每次导出的最大数据条数,比如控制在1000条
- 通过时间搜索等筛选减少每次导出的数据量,分批次导出
- 分页导出,每次只导出当前页,每页显示多一点数据
但是这样往往不能满足运营人员的需求,虽然做了优化,后续还是会偶尔出现超时等问题。这种时候优化的思路往往是同步导出优化为异步导出。
异步导出
流程图
思路其实很简单,主要步骤如图:
- 保存导出时的搜索查询条件,以及要导出的字段到mysql,同时写入redis队列里
- 然后开启后台进程或线程去执行导出操作
- 将导出的本地excel文件上传到云服务,然后回写excel文件地址到mysql
- 页面增加导出记录项,提供导出文件下载的入口展示
数据库设计
CREATE TABLE `export` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
`source` varchar(20) NOT NULL DEFAULT '' COMMENT '来源,业务区分',
`shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '门店ID',
`progress` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '导出进度:-1:未执行, 0:正在执行, 1:完全成功, 2:部分失败',
`condition` text COMMENT '搜索条件json',
`download_url` varchar(255) NOT NULL DEFAULT '' COMMENT '下载文件地址',
`export_result` varchar(255) NOT NULL DEFAULT '' COMMENT '导出结果',
`operator` varchar(20) NOT NULL COMMENT '操作人',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_shop_id_source` (`shop_id`,`source`)
) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='文件导出下载表';
代码实现
代码可自行实现,可以起多线程,php的话如果用到swoole可以起多个协程处理。代码逻辑就是写入mysql后,同步写入redis一份,然后脚本跑的时候每次lpop出来一个进行处理,导出的excel文件上传到云服务(oss、七牛等)
页面展示
用MQ异步执行
以上除了mysql + redis,然后定时脚本执行的方式外,当然了用MQ也是比较好的选择,直接将导出任务发送消息到MQ,由mq里做整个的异步导出处理