应用
是用英文写的
.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;
}
}
}
}
}