前言
前些天分享了一个导出数据到 Excel 的方案(参见前文 分享一个导出数据到 Excel 的解决方案),公司里有几位童鞋问了 2 个比较有意思的问题:
- 问题 1:
导出数据到 Excel 的最常见的场景,就是在一个列表页中,用户习惯先查询一次数据显示在列表中,然后再查询一次数据进行导出的操作,这样相当于查询了两次数据库,如果数据比较大,数据库压力就比较大,有没有方法优化性能?
- 问题 2:
ASP.NET Web Api 要导出的数据中包括有图片,当导出的数据超过 1000 条时,32G 的内存基本就被占满了,使用多线程或者异步,都没有改善,有没有办法进行优化?
关于问题 1的思考和总结
先查询数据显示在列表,然后再查询数据进行导出,确实是很常见的业务场景,
它们的查询方法基本上是相同的,区别在于显示在列表的数据通常会做分页处理,即一次只查预定义好的数量(比如 10 条)的数据,而后者则往往需要查出全部数据,
所以往往需要两个方法来进行处理,
这样除了数据比较大,多次查询有性能问题之外,
还有 SQL 语句维护的问题,比如页面增加一个查询条件,或者增加一个字段,则两个方法就都需要修改,而在实践中,程序员往往会漏了其中一个修改,在上线后带来问题。
但是这样做其实也是有一定的合理的原因的,比如用户不一定会导出数据,多页的小批量多次查询,对数据库的压力不会很大等等。
对于这个问题的性能优化,我倒是想到的一个优化方法,
就是一次查出全部的数据,将它存储在中间件中,比如内存、Redis 或其它存储器中,
分页和导出的时候,直接操作存在中间件中的数据,
由于是在内存中,速度会很快,对数据库的请求也只有一次,大大地减轻数据库的压力,
但是这样也有一些弊端,比如数据的保存和释放时机等等,
关于问题 2的思考和总结
问题 2 我觉得可以通过使用分布式进行优化,具体优化步骤如下:
- 创建一个任务表,目的是存储导出任务,充当中间件的用途。
- ASP.NET Web Api 直接将导出任务和查询数据的 SQL 语句存储到任务表,然后返回一个状态,告诉用户正在排队导出中,过一段时间再刷新页面查看导出结果。
- 在另一个机器(只做导出任务),部署一个 Console 程序(或 Windows 服务),开启 2 个线程,其中一个线程每隔一段时间就扫描一次任务表,如果有任务,就将任务放进队列中;另外一个线程就循环这个队列,执行 SQL 语句,导出数据。
- 每导出结束,就更改任务表的状态,将导出的文件放到指定的目录。
这样有 2 个好处:
- 导出和 WEB 分开两个机器,导出的任务不会占用 WEB 机器的内存,不影响网站的服务。
- 统一管理导出任务,队列每次只进行一次导出任务,反而性能更好。WEB 可能不同用户同一时间段有不同的导出行为,反而会让 WEB 服务器挂掉。
写在末尾
你觉得我提供的这两个优化方案可不可行呢?你有更好的方案吗?欢迎留言讨论
往期精彩
我是老杨,一个奋斗在一线的资深研发老鸟,让我们一起聊聊技术,聊聊程序人生,共同学习,共同进步