一次查询导出的优化

December 17, 2023
测试
测试
测试
测试
3 分钟阅读

先看效果

为了查看慢的地方,添加了一些日志记录。其中遍历1w次便会输出一次时间。

优化前

优化后

优化后

背景

  • 报表的查询导出功能,查询导出使用同一方法获取数据。
  • 页面分页查询耗时还能接收,小数据量导出也能接受。
  • 但是导出数据达到10w级别变得没法接受了。
  • 查看服务器cup使用率一直非常高(双核服务器,cup使用率一直高于50%)。

初步分析

服务器cpu使用高,很可能是数据已经获取到,计算机一直处于快速计算中。从前面日志也证实了这点。

代码分析

``

          result.ForEach(item =>
                 {
                // 省略一部分,简单的判断和赋值
                var trackingList = wobTrackingList.Where(m => m.WOBShipOrderID == item.OrderId);
    if (trackingList.IsStrictSafed())
                {
                    item.ParentTrackingNo = trackingList.FirstOrDefault(m => m.IsParentTrackingNo)?.TrackingNo;
                    var trackNos= trackingList.Where(m => !m.IsParentTrackingNo).Select(m=>m.TrackingNo);
                    item.TrackingNo = string.Join(",", trackNos);
                }
              // 省略一部分,简单的判断和字典中取值。

            // 省略一部分,字符序列化成对象然后取值赋值。前面优化后,进行测试,证明这里也不影响处理速度。
            });

``

其中 wobTrackingList也是提前查询出来的。看似都在内存中计算,应该会很快的。但是忽略了两点。1、Where 运算实际上是查询运算,虽然别人给我们封装好了,单次调用速度很快,但是也比赋值加减运算要慢。2、wobTrackingList中的数据量很可能比外层遍历的数据还要大。wobTrackingList数量越大,Where耗时越多。加之外圈遍历次数多了,累计耗时就上去了。

优化方案

将wobTrackingList 转化成字典。循环中只做取值赋值操作。

                var trackingDic = new Dictionary<Guid, (string parentTrackingNo, string trackNos)>();
                wobTrackingList.GroupBy(m => m.WOBShipOrderID).ToList().ForEach(t => {
                    var parentTrackingNo = t.FirstOrDefault(m => m.IsParentTrackingNo)?.TrackingNo;
                    var trackNos = string.Join(",", t.Where(m => !m.IsParentTrackingNo).Select(m => m.TrackingNo));
                    trackingDic.Add(t.Key, (parentTrackingNo, trackNos));
                });

              result.ForEach(item =>
                     {
                        if (trackingDic.ContainsKey(item.OrderId))
                        {
                            var (parentTrackingNo, trackNos) = trackingDic[item.OrderId];
                            item.ParentTrackingNo = parentTrackingNo;
                            item.TrackingNo = trackNos;
                        }
                });

思考

  • 出现效率问题的地方,多数都和循环处理数据有关。
  • 尽量减少循环中的复杂逻辑,只做简单的取值赋值,判断和计算。
  • 循环中处理数据,特别是耗时严重的操作(如查询数据库),要考虑好循环可能出现的次数。
  • 循环中内存取值,用字典要比list效率高。
  • 面对可能出现1w次循环的地方一定要多思考,多检查代码。

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多