本来导出想用fastadmin自带的导出功能,但是因为我数据量有16w,一导出就报内存溢出。问题的原因是我用了get去获取全部数据。因为10w+的数据全部加载到内存中,导致了内存的溢出。后来想到了tp有一个cursor游标查询,然后laravel也有一个,先看下laravel文档对游标查询的介绍
随后,我就使用了cursor进行导出,但是导出的时候,程序一直报超时。所以现在问题的瓶颈不在于查数据的内存部分了,而在于写入文件那块,在网上查找了下,发现了一个php扩展PHP-Xlswriter。文档:xlswriter-docs.viest.me/zh-cn/kuai-…。
这个扩展是一个用C写的扩展,速度很快。原来用 PHPSpreadSheet ,导出需要几分钟的时间。换了 Xlswriter 只需要十几秒。
现在查数据瓶颈解决了,写入文件的瓶颈也解决了。但是还有一个问题,就是在导出期间,用户是不能进行其他操作的。因为导出属于耗时操作,为了解决这个问题,想到了用异步队列去完成。流程是把查数据和写数据都放入到队列里面执行,用户点击下载后,将任务推到队列里,这样文件在生成中,也不会影响到用户进行其他操作。是不是看起来好了一丢丢?
但是问题来了,laravel导出后,即文件写入完成后,我是调用了
return Storage::disk('public')->download('data/'.$fileName);难不成把这个也放入到队列里面?队列任务可是不能执行return的呀。
解决方案是:在用户请求我的导出接口的时候,我生成一个task_id,并返回给前端。随后把导出任务和task_id推入队列里面,前端收到task_id后显示正在导出。然后在队列的任务类里面,因为他导出成功后,会返回下载链接,这时候就把下载链接存到接受到的task_id作为key的缓存中。然后前端收到task_id后轮询一个检查是否导出成功的接口,这个接口就是拿task_id去缓存里面查有没有值(值就是导出成功后存入到缓存的下载链接),如果有即导出成功了。前端轮询接口知道收到文件路径,如果收到则下载,显示导出完成给用户,未收到则还是显示正在导出。
以上就是我对这次大文件导出的思路和解决方案,非常感谢你能看到这里