代码之家  ›  专栏  ›  技术社区  ›  Ryan Wilson

ADO的性能。Net中的大型数据检索。NET核心web应用程序

  •  1
  • Ryan Wilson  · 技术社区  · 4 年前

    应用 是用英文写的 .NET核心2.1 数据库 SQL Server 2014 .

    有一个页面必须从SQL Server检索8万到10万行数据。我已经为我认为合适的列添加了索引,查询可以在大约5秒钟内返回所有行。因此,由于应用程序随后必须迭代所有这些行并创建视图模型对象,因此需要另外6-8秒来循环并执行所有必要的逻辑。这导致总加载时间约为16-17秒。

    在任何人建议使用分页之前,这个视图被设计成一个Excel工作表的样子,并通过一系列CSS有趣的功能使其看起来更美观。由于我在后端找不到任何更多的循环优化,并且我已经压缩了通过IIS Compress Dynamic选项返回的视图,所以我认为唯一可以节省一些时间的方法是批量查询数据库。我测试了一次处理10000个批次的运行,结果在不到1秒的时间内返回到SQL管理中。所以我把东西编了个代码 Task<DataTable> 这将创建自己的数据库连接,并对其10000条记录进行查询,并在循环中创建足够多的任务,以覆盖需要返回的记录量。这适用于上一个查询,该查询返回给定记录范围的最小和最大标识值,该范围不在付款期的日期范围内。然后我做一个 await Task.WhenAll() 在这些任务中的每一个任务上,我们都认为这将改善从SQL中最初提取数据的等待时间。我发现这会导致大致相同甚至更糟的等待时间。

    关于如何提高绩效,目前我没有任何想法,希望有人能提出另一个想法。我会提供我的 任务<DataTable> 调用数据库获取数据块的方法,这样您就可以看到我是否做错了什么。

    public async Task<IEnumerable<DataTable>> GetOverViewInfoChunk(List<int> disciplineIds, List<KeyValuePair<string, bool>> statuses, DateTime startWeek, DateTime endWeek, long firstId, long lastId, int batchNumber = 10000)
    {
         DateTime now = DateTime.Now;
         string procName = _config.GetValue<string>(nameof(GetOverViewInfoChunk));
         IList<Task<DataTable>> tablelistTasks = new List<Task<DataTable>>();
         long rangeCounter = firstId + batchNumber - 1;
         IEnumerable<DataTable> tables = new List<DataTable>();
         while (true)
         {
              Dictionary<string, string> @params = new Dictionary<string, string>();
              @params.Add("@disciplines", _sqlHelper.GetDisciplinesForTopAndBottom3(disciplineIds).ToString());
              @params.Add("@startWeekDate", startWeek.ToString("yyyy-MM-dd"));
              @params.Add("@lastWeekDate", endWeek.ToString("yyyy-MM-dd"));
              @params.Add("@jobStatuses", _sqlHelper.GetJobOverviewStatuses(statuses).ToString());
              @params.Add("@firstId", firstId.ToString());
              @params.Add("@lastId", rangeCounter > lastId ? lastId.ToString() : rangeCounter.ToString());
              tablelistTasks.Add(new Classes.SQLActionHelper(_config.GetValue<string>("Data:XXXX")).GetBatch(procName, @params));
                    
               //Increment range vars
               firstId += batchNumber;
               rangeCounter += batchNumber;
                    
               if (firstId > lastId)
                 break;
          }
             
          try
          { 
              tables = await Task.WhenAll(tablelistTasks);
          }
          catch (Exception ex)
          {
    
          }
    
          TimeSpan benchMark = DateTime.Now - now;
          return tables;
    }   
    
    //class for querying the data in batches
    public class SQLActionHelper
    {
        private string _connStr;
    
        public SQLActionHelper(string connStr)
        {
            _connStr = connStr;
        }
    
        public async Task<DataTable> GetBatch(string procName, Dictionary<string, string> @params)
        {
            DataTable dt = null;
            //create the connection object and command object
            using (SqlConnection conn = new SqlConnection(_connStr))
            {
                using (SqlCommand command = new SqlCommand(procName, conn))
                {
                    //Open the connection
                    await conn.OpenAsync();
    
                    //its a stored procedure
                    command.CommandType = System.Data.CommandType.StoredProcedure;
    
                    //Add key value pairs to the command for passing parameters to sql proc or query
                    foreach (KeyValuePair<string, string> kvp in @params)
                    {
                        command.Parameters.AddWithValue(kvp.Key, kvp.Value);
                    }
    
    
                    using (SqlDataReader reader = await command.ExecuteReaderAsync())
                    {
                        dt = new System.Data.DataTable();
                        dt.Load(reader);
                        return dt;
                    }
                }
            }
        }
    }
    
    0 回复  |  直到 4 年前
        1
  •  0
  •   Ryan Wilson    4 年前

    响应时间慢的原因不是因为糟糕的查询计划,或者没有对数据库表进行索引,或者需要分页,或者技术效率低下。问题是我忽略了最基本的问题。 多余数据 .

    虽然我承认我忽略了这样一个明显的设计缺陷有点尴尬,但我认为重要的是要记住,在存储数据时,尤其是当数据需要检索并在以后呈现给用户时,首先要看的是:“什么是必要的?”。虽然现在数据库存储相对便宜,但检索和呈现数据库的成本却不高。

    我理解这篇文章的全部内容都是导致关闭的原因,因为它本身不是可复制的或编码问题,而是一个设计缺陷。也许它会提出一些对SO用户有用的建议。我将把这个决定留给社区和版主。感谢那些提出意见的人。