记一次简单接口优化

70 阅读2分钟

维护公司老项目,所用技术为ASP.NET,某张报表点击链接后查询当天数据(共100条左右),需查询1分钟以上,故进行问题排查与优化,拉取代码后用Visual Studio打开,启动其自带的【性能探查器】勾选【检查选项】,之后本地打开项目网站,点击问题报表链接,待报表加载完成后停止分析,查看报告。
报告内容分析: image.png
通过报告很容易定位到相关的Controller中的方法,大部分耗时都在这两个方法中,通过分析这两个方法以及调用的位置,找到了出现问题的原因,源代码型如下:

    foreach(Item item in Items){
        //此处Model代指出现问题的实体类,该实体类为树状结构,有字段ParentId等
        //LoadById(int id)方法为通过Id查询数据库获取该实体类
        //GetAllSub()方法为静态方法,model.GetAllSub()可获取该实体类的所有树状子节点集合 返回List<Model>
        Model model =LoadById(item.ModelId);
        if(model!=null){
            List<Model> subModels = model.GetAllSub();
            for(Model model in subModels){
                //逻辑处理
            }
        }
     }

通过这段代码其实不难看出问题所在,在一个循环中先查询了一个实体类,再去查询实体类的所有下级子节点集合,这样操作频繁的进行数据库的查询,花费了大量的时间。在性能报告中显示,查询调用了近1w多次,一次查询报表操作花费70多秒。

c8dce9eec64a4996c4bdd70b5b1d933.png
问题定位后,解决起来还是比较简单的,目前我所使用的方法是将对应实体类一次性查出,再在代码中建立起相应的关系,这样只需查询一次数据库。

    List<Model> models = QueryModelByCondition();
    //List转换为以Model.Id为Key,Model为Value的数据字典
    Dictionary<int, Model> modelDictionary = models.ToDictionary(model => model.Id);
    //以Model.Id为Key,该节点所有子节点的集合为Value的数据字典。
    Dictionary<int, List<Model>> subModelDictionary = new Dictionary<int, List<Model>>();
    foreach(Model model in models){
        int key = model.Id;
        if(!subModelDictionary.ContainsKey(key)){
            //若没有对应key,则初始化一个
            subModelDictionary.add(key,new List<Model>());
        }
           //将符合条件的当前元素添加进入集合
           if(model.ParentId!=0&&subModelDictionary.ContainsKey(model.ParentId)){
               subModelDictionary[key].add(model);
           }
           
    }
      foreach(Item item in Items){
      
       // Model model =LoadById(item.ModelId);
       
       Model model = null;
       if(modelDictionary.ContainsKey(item.ModelId)){
           model=modelDictionary[item.ModelId];
       }
        if(model!=null){
            //List<Model> subModels = model.GetAllSub();
            List<Model> subModels = new List<Model>();
            if(subModelDictionary.ContainsKey(item.ModelId)){
                 subModels=subModelDictionary[item.ModelId];
            }
            for(Model model in subModels){
                //逻辑处理
            }
        }
     }
    

通过代码处理,减少了数据库查询次数,优化后: image.png 总耗时从70多s降到了0.8s。