Entity Framework 核心首次查询与后续查询性能
Entity Framework Core first query vs subsequent query performance
最近我一直在想,在Entity Framework中第一次查询和随后的相同查询性能之间的巨大差异背后的原因是什么?是因为 Entity Framework 有某种内部缓存来缓存查询结果,并且对同一查询的所有后续调用都使用缓存的结果吗?
为了正确理解这个问题,下面是我目前运行
的一些测试
for (var i = 0; i < 20; i++)
{
var sw = Stopwatch.StartNew();
var departments = _sampleContext.Departments.AsNoTracking().ToList();
sw.Stop();
Console.WriteLine($"Run {i+1} took {sw.ElapsedMilliseconds} ms");
}
输出:
Run 1 took 2708 ms
Run 2 took 350 ms
Run 3 took 421 ms
Run 4 took 300 ms
Run 5 took 329 ms
Run 6 took 319 ms
Run 7 took 301 ms
Run 8 took 303 ms
Run 9 took 310 ms
Run 10 took 342 ms
Run 11 took 284 ms
Run 12 took 322 ms
Run 13 took 359 ms
Run 14 took 297 ms
Run 15 took 291 ms
Run 16 took 288 ms
Run 17 took 268 ms
Run 18 took 309 ms
Run 19 took 299 ms
Run 20 took 298 ms
在这里你可以看到第一和第二之间有很大的区别运行。
这背后的原因可能是什么?
除此之外,我还尝试在不使用 Entity Framework 的情况下复制相同的场景,如下所示
var sqlConnection = new SqlConnection(_configuration.GetSection("ConnectionStrings:DefaultConnection").Value);
sqlConnection.Open();
for (var i = 0; i < 20; i++)
{
var sw = Stopwatch.StartNew();
var sqlCommand = new SqlCommand("select * from Departments", sqlConnection);
using var sqlDataReader = sqlCommand.ExecuteReader();
if (sqlDataReader.HasRows)
{
while (sqlDataReader.Read())
{
var id = sqlDataReader.GetInt32(0);
var name = sqlDataReader.GetString(1);
}
}
sw.Stop();
Console.WriteLine($"Run {i + 1} took {sw.ElapsedMilliseconds} ms");
}
输出:
Run 1 took 499 ms
Run 2 took 300 ms
Run 3 took 276 ms
Run 4 took 275 ms
Run 5 took 273 ms
Run 6 took 256 ms
Run 7 took 288 ms
Run 8 took 309 ms
Run 9 took 285 ms
Run 10 took 280 ms
Run 11 took 292 ms
Run 12 took 308 ms
Run 13 took 283 ms
Run 14 took 267 ms
Run 15 took 290 ms
Run 16 took 276 ms
Run 17 took 277 ms
Run 18 took 286 ms
Run 19 took 283 ms
Run 20 took 273 ms
这里也有第一和第二的区别运行,但没有Entity Framework.
那么剧烈
我正在使用 EF Core 3.1
根据@Larnu 的评论,可能是因为 SQL 服务器的查询计划缓存,所以我尝试 运行 每次迭代中的查询略有不同,如下所示
ADO.NET版本
var sqlCommand = new SqlCommand($"select * from Departments where ID > {i}", sqlConnection);
输出:
Run 1 took 494 ms
Run 2 took 274 ms
Run 3 took 304 ms
Run 4 took 276 ms
Run 5 took 731 ms
Run 6 took 475 ms
Run 7 took 576 ms
Run 8 took 276 ms
Run 9 took 275 ms
Run 10 took 291 ms
Run 11 took 271 ms
Run 12 took 253 ms
Run 13 took 269 ms
Run 14 took 262 ms
Run 15 took 270 ms
Run 16 took 303 ms
Run 17 took 261 ms
Run 18 took 296 ms
Run 19 took 275 ms
Run 20 took 661 ms
Entity framework版本:
var departments = _sampleContext.Departments.Where(x=>x.ID > i).AsNoTracking().ToList();
输出:
Run 1 took 2377 ms
Run 2 took 274 ms
Run 3 took 272 ms
Run 4 took 260 ms
Run 5 took 276 ms
Run 6 took 281 ms
Run 7 took 319 ms
Run 8 took 506 ms
Run 9 took 318 ms
Run 10 took 265 ms
Run 11 took 269 ms
Run 12 took 276 ms
Run 13 took 283 ms
Run 14 took 256 ms
Run 15 took 253 ms
Run 16 took 258 ms
Run 17 took 277 ms
Run 18 took 298 ms
我想第一次和第二次查询之间的差异仍然很大,所以这仅仅是因为 SQL 服务器的查询计划缓存还是其他原因?
我认为这会回答您的问题:https://github.com/dotnet/efcore/issues/9347
长话短说,第一个 运行 的上下文模型构建需要时间。模型越大,花费的时间越多。
您可以发现以下发现:
Total ms to first query 126078
Total ms to second query 17
Total ms to first save 121
Total ms to second save 10
团队知道第一个查询中的模型构建需要时间,他们已经稍微缓解了它,但正如评论中提到的:
We've improved the model building perf a bit, but your best bet would be waiting for Compiled Models #1906
最近我一直在想,在Entity Framework中第一次查询和随后的相同查询性能之间的巨大差异背后的原因是什么?是因为 Entity Framework 有某种内部缓存来缓存查询结果,并且对同一查询的所有后续调用都使用缓存的结果吗?
为了正确理解这个问题,下面是我目前运行
的一些测试for (var i = 0; i < 20; i++)
{
var sw = Stopwatch.StartNew();
var departments = _sampleContext.Departments.AsNoTracking().ToList();
sw.Stop();
Console.WriteLine($"Run {i+1} took {sw.ElapsedMilliseconds} ms");
}
输出:
Run 1 took 2708 ms
Run 2 took 350 ms
Run 3 took 421 ms
Run 4 took 300 ms
Run 5 took 329 ms
Run 6 took 319 ms
Run 7 took 301 ms
Run 8 took 303 ms
Run 9 took 310 ms
Run 10 took 342 ms
Run 11 took 284 ms
Run 12 took 322 ms
Run 13 took 359 ms
Run 14 took 297 ms
Run 15 took 291 ms
Run 16 took 288 ms
Run 17 took 268 ms
Run 18 took 309 ms
Run 19 took 299 ms
Run 20 took 298 ms
在这里你可以看到第一和第二之间有很大的区别运行。
这背后的原因可能是什么?
除此之外,我还尝试在不使用 Entity Framework 的情况下复制相同的场景,如下所示
var sqlConnection = new SqlConnection(_configuration.GetSection("ConnectionStrings:DefaultConnection").Value);
sqlConnection.Open();
for (var i = 0; i < 20; i++)
{
var sw = Stopwatch.StartNew();
var sqlCommand = new SqlCommand("select * from Departments", sqlConnection);
using var sqlDataReader = sqlCommand.ExecuteReader();
if (sqlDataReader.HasRows)
{
while (sqlDataReader.Read())
{
var id = sqlDataReader.GetInt32(0);
var name = sqlDataReader.GetString(1);
}
}
sw.Stop();
Console.WriteLine($"Run {i + 1} took {sw.ElapsedMilliseconds} ms");
}
输出:
Run 1 took 499 ms
Run 2 took 300 ms
Run 3 took 276 ms
Run 4 took 275 ms
Run 5 took 273 ms
Run 6 took 256 ms
Run 7 took 288 ms
Run 8 took 309 ms
Run 9 took 285 ms
Run 10 took 280 ms
Run 11 took 292 ms
Run 12 took 308 ms
Run 13 took 283 ms
Run 14 took 267 ms
Run 15 took 290 ms
Run 16 took 276 ms
Run 17 took 277 ms
Run 18 took 286 ms
Run 19 took 283 ms
Run 20 took 273 ms
这里也有第一和第二的区别运行,但没有Entity Framework.
那么剧烈我正在使用 EF Core 3.1
根据@Larnu 的评论,可能是因为 SQL 服务器的查询计划缓存,所以我尝试 运行 每次迭代中的查询略有不同,如下所示
ADO.NET版本
var sqlCommand = new SqlCommand($"select * from Departments where ID > {i}", sqlConnection);
输出:
Run 1 took 494 ms
Run 2 took 274 ms
Run 3 took 304 ms
Run 4 took 276 ms
Run 5 took 731 ms
Run 6 took 475 ms
Run 7 took 576 ms
Run 8 took 276 ms
Run 9 took 275 ms
Run 10 took 291 ms
Run 11 took 271 ms
Run 12 took 253 ms
Run 13 took 269 ms
Run 14 took 262 ms
Run 15 took 270 ms
Run 16 took 303 ms
Run 17 took 261 ms
Run 18 took 296 ms
Run 19 took 275 ms
Run 20 took 661 ms
Entity framework版本:
var departments = _sampleContext.Departments.Where(x=>x.ID > i).AsNoTracking().ToList();
输出:
Run 1 took 2377 ms
Run 2 took 274 ms
Run 3 took 272 ms
Run 4 took 260 ms
Run 5 took 276 ms
Run 6 took 281 ms
Run 7 took 319 ms
Run 8 took 506 ms
Run 9 took 318 ms
Run 10 took 265 ms
Run 11 took 269 ms
Run 12 took 276 ms
Run 13 took 283 ms
Run 14 took 256 ms
Run 15 took 253 ms
Run 16 took 258 ms
Run 17 took 277 ms
Run 18 took 298 ms
我想第一次和第二次查询之间的差异仍然很大,所以这仅仅是因为 SQL 服务器的查询计划缓存还是其他原因?
我认为这会回答您的问题:https://github.com/dotnet/efcore/issues/9347
长话短说,第一个 运行 的上下文模型构建需要时间。模型越大,花费的时间越多。
您可以发现以下发现:
Total ms to first query 126078
Total ms to second query 17
Total ms to first save 121
Total ms to second save 10
团队知道第一个查询中的模型构建需要时间,他们已经稍微缓解了它,但正如评论中提到的:
We've improved the model building perf a bit, but your best bet would be waiting for Compiled Models #1906