EF Core中QueryTrackingBehavior.NoTracking的适用场景及性能疑问
DbContext默认无跟踪配置的性能实践指南
一、默认设为NoTracking的性能提升到底有多大?
- 如果你的API里只读查询占比高(比如列表展示、数据统计这类接口),性能提升是看得见的:EF Core不用给查出来的每个实体维护状态快照、变更跟踪记录,能减少不少内存占用——尤其是批量查大量数据的时候,还能降低垃圾回收的负担。单条小数据查询可能感觉不到,但高频调用的只读接口跑久了,响应速度和服务器资源占用都会有改善。
- 如果是读写各半或者写操作更多的场景,提升就不明显了,反而可能因为每次更新都要手动开跟踪多写一行代码,有点麻烦。
二、只跟踪需要更新的实体,开销大吗?
- 完全不用担心,单实体更新的开销可以忽略不计:不管是用
AsTracking()单独给某条查询开跟踪,还是直接用Update()/Attach()把实体附加到上下文,EF Core只会给这个目标实体创建一条跟踪记录,内存和CPU占用都微乎其微。 - 就算是批量更新,只要不是一次性跟踪几千上万个实体,根本不会有性能问题——EF Core的跟踪系统针对单实例操作做了优化,只有极端场景下才会出现资源占用上升。
三、同行们一般怎么处理?
- 面向前端的查询型API:几乎都是默认设为
NoTracking,需要更新的时候再单独开跟踪或者直接用主键更新。一来能避免你之前遇到的实体跟踪冲突问题,二来能省不少不必要的开销。 - 后台管理这类写操作多的系统:大多默认保持跟踪,因为频繁修改实体,每次手动开跟踪反而麻烦,这点开销相对于开发效率来说不值一提。
- 折中方案:也有团队会创建两个DbContext,一个默认无跟踪用于只读查询,一个默认跟踪用于写操作,但这种做法会增加维护成本,一般小项目没必要。
四、对你当前配置的建议
- 直接保留默认
NoTracking就好:你已经解决了之前的跟踪冲突问题,而且这个配置能持续帮你降低只读场景的资源开销。 - 处理更新的两种简单方式:
- 要修改的实体,查询时加
AsTracking():比如dbContext.Users.Where(u => u.Id == userId).AsTracking().FirstOrDefault(),改完直接调用SaveChanges()就行。 - 不用查询直接更新:如果只改少数字段,直接构造实体用
Update(),比如var user = new User { Id = userId, NickName = newNickName }; dbContext.Users.Update(user); dbContext.SaveChanges(),这种方式连查询都省了,效率更高。
- 要修改的实体,查询时加
内容的提问来源于stack exchange,提问作者Joe King
相关产品推荐
相关产品推荐

