EF Core调用的SELECT查询在MSSQL挂起,SSMS执行正常求排查方向
EF Core查询C#应用超时但SSMS执行正常的排查方向
咱们来聊聊这个棘手的问题:同样的EF Core生成的SELECT查询,在SSMS里跑仅需数毫秒,但放到C#应用中就直接超时。查看SQL Server的SPID状态,发现它一直处于挂起状态,等待原因依次是IO_COMPLETION、SOS_YIELD_XXX,最后停留在PAGEIOLATCH_SH,而且这个问题只在特定参数(比如某个特定的CurrencyCode)下才会出现,其他参数完全正常。
问题相关代码与生成的SQL
EF Core查询代码
var bookings = context.Booking .Where(booking => booking.ConsigneeNumber == customer.GetCustomerTarget().Code && booking.CreatedAt >= from && booking.CreatedAt < to && booking.BookingLine.Any(b => b.BookingLineSpecification.Any(c => c.CurrencyCode == code)) ) .Include(booking => booking.BookingLine) .ThenInclude(bl => bl.BookingLineSpecification) .ThenInclude(bls => bls.UnitType) .Include(booking => booking.BookingLine) .ThenInclude(bl => bl.BookingLineAddress) .ThenInclude(bla => bla.Country) .Include(booking => booking.BookingLine) .ThenInclude(bl => bl.BookingLineAddress) .ThenInclude(bla => bla.PostalCode) .Include(booking => booking.BookingLine) .ThenInclude(bl => bl.BookingLineSpecification) .ThenInclude(bls => bls.RelBookingLineSpecificationSalesInvoiceDetail) .ThenInclude(Rel => Rel.SalesInvoiceDetail);
生成的SQL语句
(@__GetCustomerTarget_Code_0 bigint,@__from_1 datetime2(7),@__to_2 datetime2(7),@__code_3 varchar(255)) SELECT [booking].[Id], [booking].[booking_provider_id], [booking].[booking_status_id], [booking].[consignee_name], [booking].[consignee_number], [booking].[created_at], [booking].[created_by], [booking].[currency_code], [booking].[deliveryNumber], [booking].[description], [booking].[destroyed_at], [booking].[destroyed_by], [booking].[inter_company_number], [booking].[invoicee_name], [booking].[invoicee_number], [booking].[is_create], [booking].[location_id], [booking].[location_name], [booking].[maturity_level_id], [booking].[number], [booking].[order_number], [booking].[provider_key], [booking].[shipment_id], [booking].[system_responsible_id], [booking].[updated_at], [booking].[updated_by] FROM [Integration].[booking] AS [booking] WHERE ((([booking].[consignee_number] = @__GetCustomerTarget_Code_0) AND ([booking].[created_at] >= @__from_1)) AND ([booking].[created_at] < @__to_2)) AND EXISTS ( SELECT 1 FROM [Integration].[booking_line] AS [b] WHERE EXISTS ( SELECT 1 FROM [Integration].[booking_line_specification] AS [c] WHERE ([c].[currency_code] = @__code_3) AND ([b].[Id] = [c].[booking_line_id])) AND ([booking].[Id] = [b].[booking_id]))
已尝试的操作
- 在Visual Studio 2017中运行测试
- 切换到Release模式执行
- 启用/禁用延迟加载,调整Include的使用方式
- 重建执行计划要求的索引
排查方向建议
- 检查参数嗅探问题:这是跨环境执行差异最常见的原因。SSMS和C#应用的连接上下文可能让SQL Server生成不同的执行计划,尤其是特定参数下。可以尝试在查询开头添加
OPTION (RECOMPILE)强制重新生成计划,或者用OPTION (OPTIMIZE FOR (@__code_3 UNKNOWN))避免针对特定参数生成的“坏计划”。 - 对比连接设置差异:虽然已经给出了应用的连接设置,但可以仔细核对SSMS的连接参数(比如
SET ARITHABORT、事务隔离级别等)和C#应用的是否完全一致。细微的设置差异可能导致执行计划的选择截然不同。 - 深挖等待类型的根因:
PAGEIOLATCH_SH通常表示磁盘IO等待,意味着SQL Server在等待从磁盘读取数据页。可以检查:- 对应表的索引碎片情况,即使重建了索引,特定参数对应的索引页是否仍存在碎片化问题
- 服务器的磁盘IO性能,是否在应用执行时段出现IO瓶颈
- 内存压力:如果SQL Server内存不足,会导致频繁的页交换到磁盘,引发IO等待
- 捕获实际执行计划对比:在C#应用执行查询时,用SQL Profiler或者Extended Events捕获实际执行计划,和SSMS中执行的计划对比,看看是否存在索引选择、扫描/查找方式的差异。
- 验证参数类型匹配:确认EF Core传递的参数类型和SQL Server表中字段的类型完全匹配。比如
currency_code如果是nvarchar类型,而EF传递的是varchar,会导致隐式转换,无法有效使用索引,进而引发性能问题。 - 拆分查询优化复杂度:当前Include层级较多,EF Core可能生成过于复杂的单查询。可以尝试使用
AsSplitQuery()将查询拆分为多个小查询,降低单查询的执行复杂度,看看是否能缓解超时问题。
内容的提问来源于stack exchange,提问作者Morten Bork
相关产品推荐
相关产品推荐

