执行 DbCommand 失败,因为超时已过期 .net 核心

Posted

技术标签:

【中文标题】执行 DbCommand 失败,因为超时已过期 .net 核心【英文标题】:Failed executing DbCommand because timeout expired .net core 【发布时间】:2020-05-15 23:10:36 【问题描述】:

我的目标是提供简单的 API 来从包含 5 列的 Payments(约 400 行)表中检索数据。

Payment: Id (int),
PaymentsNumber (tinyint),
Rate (decimal(18,2)),
ProductType (tinyint),
ClientClubType (tinyint).

用户可以使用以下请求参数发出帖子请求(应该返回约 12 行):

PaymentsRequest 

    public int? PaymentsNumber  get; set; 
    public byte? ProductType  get; set; 
    public byte? ClientClubType  get; set; 

使用 EF-Core:

services.AddDbContext<MyContext>(cfg => cfg.UseSqlServer(Configuration.GetConnectionString(...),optionsBuilder => optionsBuilder.CommandTimeout(60)));

public async Task<IEnumerable<Payments>> GetPaymentsAsync(PaymentsRequest request)

    IQueryable<Payments> query = this._context.Set<Payments>();
    query = query.Where(filter => 
                        (request.ClientClubType == null || filter.ClientClubType == request.ClientClubType) &&
                        (request.ProductType == null || filter.ProductType == request.ProductType) &&
                        (request.PaymentsNumber == null || filter.PaymentsNumber == request.PaymentsNumber));

    return await query.ToListAsync();

在 Azure 应用程序洞察中,我可以看到 2 个连续日志,由同一异常引起:

    Lo​​g1:执行 DbCommand 失败。 Lo​​g2:执行超时已过期。在操作完成之前超时时间已过或服务器没有响应。

Log1是(虽然这里不需要写log2):

执行 DbCommand 失败(65,238 毫秒) [参数=[@__request_ClientClubType_0='?' (大小 = 1)(DbType = 字节),@__request_ProductType_1='?' (大小 = 1) (DbType = 字节)], CommandType='Text', CommandTimeout='60']

选择 [p].[Id], [p].[ClientClubType], [p].[PaymentsNumber], [p].[ProductType], [p].[Rate] FROM [Payments] AS [p] WHERE (([p].[ClientClubType] = @__request_ClientClubType_0) AND @__request_ClientClubType_0 不是 NULL) AND (([p].[ProductType] = @__request_ProductType_1) AND @__request_ProductType_1 不为空)

我的应用程序是部署在 azure linux webapp 上的 .net core 3.0 应用程序。

该问题仅在生产中出现,并非每次都出现,我无法从 MSSMS 中重建问题。 有什么想法吗?

更新:

@panagiotis-kanavos 发表评论后,我已将代码更新为:

services.AddDbContextPool<MyContext>(cfg => cfg.UseSqlServer(Configuration.GetConnectionString(...),optionsBuilder => optionsBuilder.CommandTimeout(60)));

public async Task<IEnumerable<Payments>> GetPaymentsAsync(PaymentsRequest request)

    IQueryable<Payments> query = this._context.Payments;
    query = query.Where(filter => 
                        (filter.ClientClubType == request.ClientClubType) &&
                        (filter.ProductType == request.ProductType) &&
                        (filter.PaymentsNumber == request.PaymentsNumber));

    return await query.ToListAsync();

【问题讨论】:

至于为什么要花这么长时间,可能是缺少索引,与其他查询的并发冲突,查询本身的问题(catch-all 查询不好,LINQ 根本不需要)。最有可能的是,由于NULLs,查询锁定了太多行,从而导致与尝试访问同一张表的其他查询发生冲突。也许也是一个长期运行的交易? 您可以使用if(someParam!=null) query=query.Where(r=&gt;r.field==param);动态添加条件。这个Wheres 序列等价于AND。如果不需要参数,就不要加条件 该表扫描现在对所有行使用 S(hared) 锁。这意味着任何想要更新的查询都必须等待其他事务完成。如果你的代码显式打开并持有一个事务(它不应该与任何 ORM 一起做),锁可能会保留很长时间。这增加了冲突并阻塞了 lot 如果您使用数据库事务来实现“每个请求的业务事务”或您不需要的工作单元,那么您的查询最终会相互阻塞。您不需要这样做,因为 ORM 的 cache 更改并且仅在调用 SaveChanges 时以原子方式将它们保存在一个批次中。你已经有一个 UoW,你不需要 DB 事务来实现它(反正不能这样做) 您能否安装 sp_whoisactive 并检查您在查询运行时有多少事务? 【参考方案1】: 您的超时时间是 60 秒,这可以增加。这样做通常不是一个好主意,因为它会隐藏其他问题。 如果该表有大量写入,或者某项事务有一个长时间运行的事务,它可能会阻塞/争用您的查询。 如果您的查询是 SQL 操作的更大的开始事务 - 结束事务序列的一部分,它可能会被其他打开的事务阻塞 与上一个相同 - 如果同时/几乎同时对该查询进行多次调用,则可能会减慢每个查询的处理速度。我已经看到了这种情况,其中 Web 前端填充了 20 行数据的屏幕,其中每一行是对相同类型数据的相同 web-api 端点的不同调用。例如,获取过去 12 个月中每个月的每月交易美元总额。

【讨论】:

以上是关于执行 DbCommand 失败,因为超时已过期 .net 核心的主要内容,如果未能解决你的问题,请参考以下文章

Linq Count() 超时 - 执行超时已过期。操作完成前超时时间已过或服务器无响应

在 SQL Server 2008 中执行视图时出现“超时已过期”错误

SqlException:超时已过期

如何在 EF Core 3.1 中使用脚手架流程更新我的实体 - 执行超时已过期

ExecuteQueryin linq:超时已过期。操作完成前超时时间已过或服务器无响应

如何解决超时过期问题?