Ef Linq 查询超时,但相同的查询在 SSMS 上不到 1 秒
Ef Linq queries timed out, but same queries less than 1 second on SSMS
首先我在 SSMS 上尝试 ARITHABORT OFF
它仍然不到 1 秒。
我使用 EntityFrameWork:6.1.3 和 Azure Sql S1 层(我会尝试使用第 3 层,如果有变化会通知您。)
我使用 EF Profiler 从 linq 生成 sql。我已经查询了我分享的所有linq,它们在SSMS上都不到1秒。
我在 AuditLog 上有 300 万条记录 Table。 ID 为 3 的一位客户有 170K 条记录,另一位 ID 为 35 的客户有 125 条记录。我将最小化代码。
审计日志模型:
public class AuditLog
{
public long? CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual CustomerSummary Customer { get; set; }
[Required]
[Index]
public DateTime CreatedDate { get; set; }
}
第一次查询:
if (customer != null)
{
var customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList();
}
如果我尝试与具有 170k 行的客户一起尝试,它会给出超时异常。如果我对有 125 条记录的客户进行尝试,没问题。
第二个查询:与第一个相同,我只是包括了客户。
if (customer != null)
{
var customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList();
}
结果与第一次查询相反。如果我尝试与拥有 170k 行的客户一起使用,那很好。如果我尝试与有 125 条记录的客户一起尝试,它会给出超时异常。
第三个查询:与第一个查询相同,但我匹配long?
customerId.
if (customer != null)
{
long? customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList();
}
结果与第一次查询相反。如果我尝试与拥有 170k 行的客户一起使用,那很好。如果我尝试与有 125 条记录的客户一起尝试,它会给出超时异常。
第四个查询:与第二个查询相同,但我在 customerId 的位置匹配 long?
。
if (customer != null)
{
long? customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList();
}
结果与第二次查询相反。如果我尝试与拥有 170k 行的客户一起尝试,它会给出超时异常。如果我对有 125 条记录的客户进行尝试,没问题。
我真的很困惑。为什么内部连接或将匹配参数更改为 long?
会改变结果?为什么这所有查询 运行 在 SSMS 上不到 1 秒,并在 ef linq 上给出错误?
错误:
{System.Data.SqlClient.SqlException (0x80131904): Timeout expired.
The timeout period elapsed prior to completion of the operation or the
server is not responding. ---> System.ComponentModel.Win32Exception
(0x80004005): The wait operation timed out at
System.Data.SqlClient.SqlConnection.OnError(SqlException exception,
Boolean breakConnection, Action`1 wrapCloseInAction)
更新 (19/04/2016):
在 Ivan Stoev 个评论建议之后。
Have you tried (just for the sake of test) using hardcoded 3 and 35 instead of customerId
variable?
我没有收到任何错误,查询速度与在 SSMS 上一样快。
更新 (20/04/2016): 真正的问题是 Parameter Sniffing。当我将参数包含或更改为可为空时,实际上我已经创建了另一个查询和另一个查询计划。我为拥有 125 条记录的客户创建了一些计划,为拥有这 4 个查询的 170k 记录的客户创建了其他计划。这就是我得到不同结果的原因。
您所遇到的是所谓的 Parameter Sniffing Problem. I don't know a simple general solution so far, so usually suggest a workaround by eliminating some of the SQL query parameters by manually binding constant values inside the expressions, like in 的结果。
对于您的场景,我建议使用以下自定义扩展方法:
public static class QueryableExtensions
{
public static IQueryable<T> WhereEquals<T, TValue>(this IQueryable<T> source, Expression<Func<T, TValue>> selector, TValue value)
{
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.Equal(selector.Body, Expression.Constant(value)),
selector.Parameters);
return source.Where(predicate);
}
}
然后像这样更新您的代码段
if (customer != null)
{
var result= Dbset.WhereEquals(x => x.CustomerId.Value, customer.Id)
.OrderByDescending(x => x.CreatedDate)
.Skip(0).Take(25)
.Include(x => x.Customer)
.ToList();
}
首先我在 SSMS 上尝试 ARITHABORT OFF
它仍然不到 1 秒。
我使用 EntityFrameWork:6.1.3 和 Azure Sql S1 层(我会尝试使用第 3 层,如果有变化会通知您。)
我使用 EF Profiler 从 linq 生成 sql。我已经查询了我分享的所有linq,它们在SSMS上都不到1秒。
我在 AuditLog 上有 300 万条记录 Table。 ID 为 3 的一位客户有 170K 条记录,另一位 ID 为 35 的客户有 125 条记录。我将最小化代码。
审计日志模型:
public class AuditLog
{
public long? CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual CustomerSummary Customer { get; set; }
[Required]
[Index]
public DateTime CreatedDate { get; set; }
}
第一次查询:
if (customer != null)
{
var customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList();
}
如果我尝试与具有 170k 行的客户一起尝试,它会给出超时异常。如果我对有 125 条记录的客户进行尝试,没问题。
第二个查询:与第一个相同,我只是包括了客户。
if (customer != null)
{
var customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList();
}
结果与第一次查询相反。如果我尝试与拥有 170k 行的客户一起使用,那很好。如果我尝试与有 125 条记录的客户一起尝试,它会给出超时异常。
第三个查询:与第一个查询相同,但我匹配long?
customerId.
if (customer != null)
{
long? customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList();
}
结果与第一次查询相反。如果我尝试与拥有 170k 行的客户一起使用,那很好。如果我尝试与有 125 条记录的客户一起尝试,它会给出超时异常。
第四个查询:与第二个查询相同,但我在 customerId 的位置匹配 long?
。
if (customer != null)
{
long? customerId = customer.Id;
var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList();
}
结果与第二次查询相反。如果我尝试与拥有 170k 行的客户一起尝试,它会给出超时异常。如果我对有 125 条记录的客户进行尝试,没问题。
我真的很困惑。为什么内部连接或将匹配参数更改为 long?
会改变结果?为什么这所有查询 运行 在 SSMS 上不到 1 秒,并在 ef linq 上给出错误?
错误:
{System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
更新 (19/04/2016):
在 Ivan Stoev 个评论建议之后。
Have you tried (just for the sake of test) using hardcoded 3 and 35 instead of
customerId
variable?
我没有收到任何错误,查询速度与在 SSMS 上一样快。
更新 (20/04/2016): 真正的问题是 Parameter Sniffing。当我将参数包含或更改为可为空时,实际上我已经创建了另一个查询和另一个查询计划。我为拥有 125 条记录的客户创建了一些计划,为拥有这 4 个查询的 170k 记录的客户创建了其他计划。这就是我得到不同结果的原因。
您所遇到的是所谓的 Parameter Sniffing Problem. I don't know a simple general solution so far, so usually suggest a workaround by eliminating some of the SQL query parameters by manually binding constant values inside the expressions, like in
对于您的场景,我建议使用以下自定义扩展方法:
public static class QueryableExtensions
{
public static IQueryable<T> WhereEquals<T, TValue>(this IQueryable<T> source, Expression<Func<T, TValue>> selector, TValue value)
{
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.Equal(selector.Body, Expression.Constant(value)),
selector.Parameters);
return source.Where(predicate);
}
}
然后像这样更新您的代码段
if (customer != null)
{
var result= Dbset.WhereEquals(x => x.CustomerId.Value, customer.Id)
.OrderByDescending(x => x.CreatedDate)
.Skip(0).Take(25)
.Include(x => x.Customer)
.ToList();
}