为何EF Core首次数据库查询耗时远高于后续查询?
为什么EF Core首次AsNoTracking查询耗时远高于后续查询?
嘿,这个现象其实挺常见的,咱们一步步拆解背后的原因:
1. ADO.NET连接池在“偷偷”复用连接
你猜的没错,但不是EF Core直接复用DbContext的连接,而是依赖ADO.NET数据库连接池的机制:
- 首次查询时,系统需要创建新的数据库连接(包括TCP握手、身份验证等步骤),这部分开销很大;
- 当查询完成后,EF Core并不会真的关闭连接,而是把它放回连接池里标记为“空闲”;
- 后续的查询会直接从连接池里取出空闲连接复用,跳过了创建连接的昂贵步骤,所以耗时骤降。
连接池是ADO.NET默认开启的功能,它会根据连接字符串的参数(比如Max Pool Size)管理连接的数量,避免频繁创建销毁连接带来的性能损耗。
2. EF Core的首次初始化开销
除了连接池的因素,首次查询时EF Core还会完成一系列一次性的初始化工作:
- 构建实体与数据库表的映射元数据(比如你的
Project实体对应哪个表、字段怎么对应),并把这些元数据缓存起来; - 解析LINQ查询表达式,生成对应的SQL语句,同样会缓存这个SQL生成结果;
- 初始化查询执行所需的内部组件。
这些工作只会在第一次查询时执行,后续查询直接复用缓存好的元数据和SQL,自然速度快很多——哪怕你用了AsNoTracking,这些全局缓存的内容也不会受影响。
3. 结合你的实验数据看
你的三次查询耗时差异正好对应了这些阶段:
- 第一次2457ms:包含了连接创建+元数据初始化+SQL生成+实际查询的全部开销;
- 第二次51ms、第三次29ms:只需要复用连接+复用缓存元数据和SQL+实际查询,开销自然大幅降低。
另外你提到的“ORM为每个请求打开/关闭连接”其实是个误解:这里的“打开/关闭”只是从连接池里获取和归还连接,并不是真正和数据库断开TCP连接——断开连接的成本太高,连接池就是为了避免这个才存在的。当你调用context.Dispose()时,DbContext只是把持有的连接归还给连接池,而不是销毁它。
内容的提问来源于stack exchange,提问作者Adam Shakhabov
相关产品推荐
相关产品推荐

