EFCore⼩坑:DbContextPool会引起数据库连接池连接耗尽
DbContextPool 是 ASP.NET Core 2.1 引⼊的新特性,可以节省创建 DbContext 实例的开销,但没有想到其中藏着⼀个⼩坑。
最近有⼀个 ASP.NET Core 项⽬持续运⾏⼀段时间后⽇志中就会出现数据库连接池达到最⼤连接数限制的错误:
System.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.  at System.Data.Common.ADP.ExceptionWithStackTrace(Exception e)
开始以为是哪个地⽅的代码造成 DbContext 不能正常 Dispose ,但在代码中没有到任何相关线索。后来实在没有其他可以怀疑的地⽅,唯有 DbContextPool ,于是尝试去掉DbContextPool ,结果错误就消失了。果然是 DbContextPool 引起的,但让⼈纳闷的是 DbContextPool 本来就是为了节省创建 DbContext 实例的开销,怎么反⽽消耗更多数据
库连接,⽽且这个项⽬的负载很低,怎么可能把整个连接池都消耗殆尽呢?
今天在周会上谈了这个怪问题,后来突然想到:每个 DbContext 实例都会占⽤⼀个数据库连接(SqlCon
nection),不启⽤ DbContextPool 的时候,请求⼀结束,对应
DbContext 实例就被 Dispose ,数据库连接就会被放回连接池。⽽使⽤ DbContextPool 的时候,请求结束后 DbContext 不会被 Dispose ⽽是被放回 DbContextPool
,DbContext 被放回属于⾃⼰的池中,就意味它对应的数据库连接不会被放回它所属的连接池。DbContextPool 中的每⼀个 DbContext 都对应⼀个数据库连接,DbContextPool
中每多⼀个 DbContext ,数据库连接池中就会少⼀个数据库连接。当这两个池的⼤⼩不⼀样且 DbContextPool ⼤于数据库连接池,问题就来了,DbContextPool 根据⾃家池
(假设是128)⼦的⼤⼩畅快地向池中填 DbContext ,浑然不顾数据库连接池的⼤⼩(假设是100),当填到第 101 个 DbContext 时就会出现上⾯的错误。
这个项⽬中⽤的都是默认设置,是不是默认设置就会触发这个问题呢?
查看 DbContextPool 的发现池的默认⼤⼩限制是 128
public static IServiceCollection AddDbContextPool<TContext>(
[NotNull] this IServiceCollection serviceCollection,
timeout was reached
[NotNull] Action<DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
where TContext : DbContext
=> AddDbContextPool<TContext, TContext>(serviceCollection, optionsAction, poolSize);
查看 SqlConnention 的发现连接池的默认⼤⼩限制是 100
internal const int Max_Pool_Size = 100;
默认设置就会触发问题,实实在在的⼀个⼩坑。
知道了原因,解决起来就很简单了,将 DbContextPool 的 poolSize 设置为⼩于数据库连接池的 Max_Pool_Size
services.AddDbContextPool<JobDb>(option =>
option.UseSqlServer(Configuration.DbConnectionStr()),
poolSize: 64);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。