6

Zuerst versuchte ich ARITHABORT OFF auf SSMS ist es immer noch weniger als 1 Sekunde.Ef Linq Abfragen abgelaufen, aber die gleichen Abfragen weniger als 1 Sekunde auf SSMS

I EntityFramework verwenden: 6.1.3 und Azure SQL-S1-Tier (Ich werde versuchen, mit Tier 3 und lassen Sie wissen, ob sich etwas ändert.)

Ich verwende EF Profiler generierte SQL von Linq zu bekommen. Ich habe alle linqs abgefragt, die ich geteilt habe, sie alle sind weniger als 1 Sekunde auf SSMS.

Ich habe 3 Millionen Datensätze auf AuditLog-Tabelle. Ein Kunde mit ID 3 hat 170.000 Datensätze, der andere Kunde mit ID 35 hat 125 Datensätze. Ich werde den Code minimieren.

auditlog Modell:

public class AuditLog 
    { 
    public long? CustomerId { get; set; } 

    [ForeignKey("CustomerId")] 
    public virtual CustomerSummary Customer { get; set; } 

    [Required] 
    [Index] 
    public DateTime CreatedDate { get; set; } 
    } 

erste Abfrage:

if (customer != null) 
    { 
     var customerId = customer.Id; 
     var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList(); 
    } 

, wenn ich mit dem Kunden versuchen, die 170k Zeilen hat, gibt es Ausnahme Auszeit. Wenn ich es mit einem Kunden versuche, der 125 Datensätze hat, ist das in Ordnung.

Zweite Abfrage: Es ist gleich mit der ersten, die ich nur Kunden einschließen.

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(); 
    } 

Das Ergebnis ist das Gegenteil der ersten Abfrage. Wenn ich es mit Kunden versuche, die 170k Zeilen haben, ist es in Ordnung. Wenn ich es mit einem Kunden versuche, der 125 Datensätze hat, gibt es eine Timeout-Ausnahme.

Dritte Abfrage: Es ist gleich mit der ersten Abfrage, aber ich stimme long? auf wo für 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(); 
    } 

Das Ergebnis ist das Gegenteil der ersten Abfrage. Wenn ich es mit Kunden versuche, die 170k Zeilen haben, ist es in Ordnung. Wenn ich es mit einem Kunden versuche, der 125 Datensätze hat, gibt es eine Timeout-Ausnahme.

Vierte query: Es ist dieselbe mit dem zweiten Abfrage, aber ich passen long? auf, wo für customerId.

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(); 
    } 

Das Ergebnis ist das Gegenteil der zweiten Abfrage. Wenn ich es mit einem Kunden versuche, der 170k Zeilen hat, gibt es eine Auszeit-Ausnahme. Wenn ich es mit einem Kunden versuche, der 125 Datensätze hat, ist das in Ordnung.

Ich bin wirklich verwirrt. Warum ändern sich die Ergebnisse für den inneren Join oder den geänderten Match-Parameter auf long?? Und warum laufen all diese Abfragen unter 1 ms auf SSMS und geben Fehler auf ef linq?

Fehler:

{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)

Update (19/04/2016):

Nach Ivan Stoev Vorschlag auf Kommentare.

Have you tried (just for the sake of test) using hardcoded 3 and 35 instead of customerId variable?

ich keine Fehler bekommen haben und Anfragen sind am schnellsten wie auf SSMS.

Update (20/04/2016): Das eigentliche Problem ist Parameter Sniffing. Wenn ich Parameter in Nullable einfügte oder änderte, habe ich tatsächlich andere Abfragen und andere Abfragepläne erstellt. Ich habe einige Pläne mit Kunden erstellt, die 125 Datensätze haben, und die anderen mit Kunden, die 170k Datensätze dieser 4 Abfragen haben. Deshalb habe ich unterschiedliche Ergebnisse bekommen.

+0

Wie vergleichen Sie die Ergebnisse? Vergleichen Sie mit dem von EF generierten SQL oder schreiben Sie selbst? –

+0

SQL generiert von EF. –

+0

Das ist interessant. Es kann nützlich sein, auch anzugeben, welche Version von EF Sie verwenden. –

Antwort

4

Was Sie erleben, ist ein Ergebnis der so genannten Parameter Sniffing Problem. Ich kenne bisher keine einfache allgemeine Lösung, daher wird normalerweise eine Umgehung vorgeschlagen, indem einige der SQL-Abfrageparameter durch manuelle Bindung von Konstantenwerten in den Ausdrücken entfernt werden, wie in EntityFramework LINQ query count fails but query returns result. How to optimize LINQ query?.

Für Ihr Szenario würde ich die folgende benutzerdefinierte Erweiterungsmethode vorschlagen:

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); 
    } 
} 

und dann Schnipsel aktualisieren ähnliche

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(); 
} 
+0

Hallo Ivan, 'var ListItems = (von pl in _context.Order wo! Pl.SentDateUTC.HasValue wählen pl) .ToList();' ist dieser Fall von Parameter Sniffing? Weil oben Linq mit Timeout-Ausnahme fehlgeschlagen ist, während Abfrage funktioniert. können Sie mir helfen, dieses Problem zu lösen? –

+0

@yogendarji Hallo, Ihr Fall ist anders, weil die Abfrage keine Parameter verwendet. Grundsätzlich suchen Sie nach 'pl.SentDateUTC == null'. Höchstwahrscheinlich führt es einen vollständigen Tabellenscan durch. Aber sollte nicht so lange dauern, wie viele Datensätze haben Sie in der db-Tabelle? –

+0

Ich habe rund 150k Datensätze in der Tabelle. Abfrageplan suggerieren, dass ich Non-Cluster-Index erstellen soll. Soll ich einen Index für diese Spalte erstellen? –