最近我做过一个MySQL百万级别
数据的excel
导出功能,已经正常上线使用了。
这个功能挺有意思的,里面需要注意的细节还真不少,现在拿出来跟大家分享一下,希望对你会有所帮助。
原始需求:用户在UI界面
上点击全部导出
按钮,就能导出所有商品数据。
咋一看,这个需求挺简单的。
但如果我告诉你,导出的记录条数,可能有一百多万,甚至两百万呢?
这时你可能会倒吸一口气。
因为你可能会面临如下问题:
-
如果同步导数据,接口很容易超时。
-
如果把所有数据一次性装载到内存,很容易引起OOM。
-
数据量太大sql语句必定很慢。
-
相同商品编号的数据要放到一起。
-
如果走异步,如何通知用户导出结果?
-
如果excel文件太大,目标用户打不开怎么办?
我们要如何才能解决这些问题,实现一个百万级别的excel数据快速导出功能呢?
1.异步处理
做一个MySQL百万数据级别的excel导出功能,如果走接口同步导出,该接口肯定会非常容易超时
。
因此,我们在做系统设计
的时候,第一选择应该是接口走异步
处理。
说起异步处理,其实有很多种,比如:使用开启一个线程
,或者使用线程池
,或者使用job
,或者使用mq
等。
为了防止服务重启时数据的丢失问题,我们大多数情况下,会使用job
或者mq
来实现异步功能。
1.1 使用job
如果使用job的话,需要增加一张执行任务表
,记录每次的导出任务。
用户点击全部导出按钮,会调用一个后端接口,该接口会向表中写入一条记录,该记录的状态为:待执行
。
有个job,每隔一段时间(比如:5分钟),扫描一次执行任务表,查出所有状态是待执行的记录。
然后遍历这些记录,挨个执行。
需要注意的是:如果用job的话,要避免重复执行的情况。比如job每隔5分钟执行一次,但如果数据导出的功能所花费的时间超过了5分钟,在一个job周期内执行不完,就会被下一个job执行周期执行。
所以使用job时可能会出现重复执行的情况。
为了防止job重复执行的情况,该执行任务需要增加一个执行中
的状态。
具体的状态变化如下:
-
执行任务被刚记录到执行任务表,是
待执行
状态。 -
当job第一次执行该执行任务时,该记录再数据库中的状态改为:
执行中
。 -
当job跑完了,该记录的状态变成:
完成
或失败
。
这样导出数据的功能,在第一个job周期内执行不完,在第二次job执行时,查询待处理
状态,并不会查询出执行中
状态的数据,也就是说不会重复执行。
此外,使用job还有一个硬伤即:它不是立马执行的,有一定的延迟。
如果对时间不太敏