424 字
2 分鐘
瀏覽次數
EF Core 查詢忽快忽慢?用 RECOMPILE 解掉 Parameter Sniffing
2025-07-25
2026-04-10

結論#

  • DbCommandInterceptor + TagWith,即可在 EF Core 查詢加上 OPTION(RECOMPILE),快速避開 Parameter Sniffing。

適合用在哪裡#

  • 查詢「同一支 SQL,但速度不穩」的場景
  • EF Core 查詢 timeout,但 SSMS 正常
  • 條件差異大(例如:熱門 vs 冷門資料)
  • 不想改 SQL,但需要快速解法

流程步驟#

1. 註冊 Interceptor#

  • 在 DbContext 注入自訂 Interceptor
  • 讓 EF Core 有機會攔截 SQL 並改寫
builder.UseSqlServer(connectionString)
.UseRecompileExtensions(); // 註冊攔截器

2. 在查詢標記 RECOMPILE#

  • TagWith 做標記(EF Core 內建)
  • 讓 Interceptor 知道哪段 SQL 要處理
var name = "SomeName";
var result = context.SomeItems
.Where(x => x.Name == name)
.WithRecompile() // 加上標記
.ToList();

實際 SQL:

SELECT [s].[Id], [s].[Name]
FROM [SomeItems] AS [s]
WHERE [s].[Name] = @__name_0
OPTION(RECOMPILE)

3. 實作 Interceptor 改寫 SQL#

  • 偵測 Tag
  • 移除 comment 並補上 OPTION(RECOMPILE)
public static class RecompileExtensions
{
private const string RecompileTag = "recompile_query_tag";
private const string RecompileComment = "-- " + RecompileTag + "\\r\\n";
public static DbContextOptionsBuilder UseRecompileExtensions(this DbContextOptionsBuilder builder)
{
return builder.AddInterceptors(RecompileInterceptor.Instance);
}
public static IQueryable<T> WithRecompile<T>(this IQueryable<T> query)
{
return query.TagWith(RecompileTag);
}
private class RecompileInterceptor : DbCommandInterceptor
{
public static RecompileInterceptor Instance = new();
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
CorrectCommand(command);
return base.ReaderExecuting(command, eventData, result);
}
private static void CorrectCommand(DbCommand command)
{
var newQuery = command.CommandText.Replace(RecompileComment, "");
if (!ReferenceEquals(newQuery, command.CommandText))
{
if (newQuery.StartsWith("\\r\\n"))
newQuery = newQuery.Substring(2);
newQuery += "\\r\\nOPTION(RECOMPILE)";
command.CommandText = newQuery;
}
}
}
}

補充#

  • RECOMPILE 每次都重新產生執行計畫,CPU 成本會增加,不適合高頻查詢
  • 適合「查詢不穩」而不是「查詢本身慢」
  • 若是全域問題,可考慮 DB 層級關閉 Parameter Sniffing
ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = OFF;

指令 / 範例整理#

Click to expand
// 註冊
builder.UseRecompileExtensions();
// 使用
query.WithRecompile().ToList();
-- DB 層級關閉 Parameter Sniffing
ALTER DATABASE SCOPED CONFIGURATION
SET PARAMETER_SNIFFING = OFF;

收尾#

  • 當 EF 跟 SQL 表現不一致時,這招通常能快速止血;但長期還是要回頭優化索引與查詢。

參考來源#

EF Core 查詢忽快忽慢?用 RECOMPILE 解掉 Parameter Sniffing
https://joyceowo.github.io/posts/23ba78ea09fa8171ba34eeb03351b2a5/
作者
JoyceOwO
發佈於
2025-07-25
許可協議
CC BY-NC-SA 4.0