Il piano di query non valide di SQL Server 2016 blocca il database una volta alla settimana


16

Una volta alla settimana, nelle ultime 5 settimane, all'incirca alla stessa ora del giorno (la mattina presto, può essere basato sull'attività dell'utente quando le persone iniziano a usarla), SQL Server 2016 (AWS RDS, mirroring) inizia a scadere molto interrogazioni.

UPDATE STATISTICS su tutte le tabelle lo risolve sempre immediatamente.

Dopo la prima volta, l'ho fatto aggiornare tutte le statistiche su tutti i tavoli ogni sera (anziché settimanalmente), ma è ancora successo (circa 8 ore dopo l'esecuzione delle statistiche di aggiornamento, ma non tutti i giorni).

L'ultima volta, ho abilitato Query Store per vedere se riuscivo a trovare quale specifico piano di query / query fosse. Penso di essere riuscito a restringerlo a uno:

Piano di query errato

Dopo aver trovato quella query, ho aggiunto un indice consigliato che mancava da questa query non utilizzata di frequente (ma che tocca molte tabelle utilizzate di frequente).

Il piano di query errato stava eseguendo una scansione dell'indice (su una tabella con solo 10k righe). Altri piani di query restituiti in millisecondi, tuttavia, eseguivano la stessa scansione. Il piano di query più recente, dopo aver creato il nuovo indice, cerca solo. Ma anche senza quell'indice, il 99% delle volte, stava tornando in pochi millisecondi, ma poi, ogni settimana, ci sarebbero voluti> 40 secondi.

Ciò ha avuto inizio dopo essere passato a SQL Server 2016 dal 2012.

DBCC CHECKDB non restituisce errori.

  1. Il nuovo indice risolverà il problema, facendo in modo che non scelga mai più il piano errato?
  2. Dovrei "forzare" il piano che funziona bene ora?
  3. Come posso assicurarmi che ciò non accada a un'altra query / piano?
  4. È questo un sintomo di un problema più grande?

Indici che ho appena aggiunto:

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Testo completo della query:

https://pastebin.com/Z5szPBfu (generato da LINQ, posso / dovrei essere in grado di ottimizzare le colonne selezionate, ma dovrebbe essere irrilevante per questo problema)


Ho appena notato che la scansione dei piani precedenti che non è scaduta era su una tabella diversa, con le stesse dimensioni. Appuntamento: 11931 righe, AppointmentAttendee: 11937 righe.
Nome del suono professionale

Risposte:


16

Risponderò alle tue domande in un ordine diverso da quello che hai chiesto loro.

4. È questo un sintomo di un problema più grande?

Il nuovo strumento per la stima della cardinalità in SQL Server 2016 potrebbe contribuire al problema. SQL Server 2012 utilizza CE legacy e non hai riscontrato problemi con quella versione. Il nuovo stimatore della cardinalità fa ipotesi diverse sui dati e può generare piani di query diversi per lo stesso SQL. È possibile che si verifichino prestazioni migliori per alcune query con CE legacy a seconda della query e dei dati. Pertanto, alcune parti del modello di dati potrebbero non corrispondere al meglio per il nuovo CE. Va bene, ma potresti aver bisogno di aggirare il nuovo CE per ora.

Mi occuperei anche di prestazioni delle query incoerenti anche con aggiornamenti quotidiani delle statistiche. Una cosa importante da notare è che la raccolta di statistiche su tutte le tabelle cancellerà efficacemente tutti i piani di query dalla cache, quindi potresti avere un problema con le statistiche o avere a che fare con lo sniffing dei parametri. È difficile prendere una decisione senza molte informazioni sul modello di dati, sulla velocità di modifica dei dati, sui criteri di aggiornamento delle statistiche, su come chiamare il codice, ecc. SQL Server 2016 offre alcune impostazioni a livello di database per lo sniffing dei parametri che potrebbero essere utili , ma ciò potrebbe influire sull'intera applicazione anziché sull'unica query problematica.

Lancerò uno scenario di esempio che potrebbe portare a questo comportamento. Tu hai detto:

Alcuni utenti potrebbero avere 1 record di autorizzazioni, alcuni fino a 20k.

Supponiamo di raccogliere statistiche su tutti i tavoli che cancellano tutti i piani di query. A seconda dei fattori sopra menzionati, se la prima query del giorno è rivolta a un utente con solo 1 record di autorizzazione, SQL Server può memorizzare nella cache un piano che funziona bene per gli utenti con 1 record ma funziona terribilmente con gli utenti con 20k record. Se la prima query del giorno è rivolta a un utente con 20k record, potresti ottenere un buon piano per 20k record. Quando il codice viene eseguito su un utente con 1 record, potrebbe non essere la query più ottimale ma potrebbe comunque terminare in ms. Suona davvero come lo sniffing dei parametri. Spiega perché non vedi sempre il problema o perché a volte ci vogliono ore per presentarsi.

1. Il nuovo indice risolverà il problema, facendo in modo che non scelga mai più il piano errato?

Penso che uno degli indici che hai aggiunto impedirà il problema perché l'accesso ai dati richiesti tramite l'indice sarà più economico rispetto all'esecuzione di una scansione dell'indice cluster contro la tabella, soprattutto quando la scansione non può terminare in anticipo. Ingrandiamo la parte negativa del piano di query:

piano di query errato

SQL Server stima che verrà restituita solo una riga dal join su [Permission]e [Project]. Per ogni riga nell'input esterno eseguirà una scansione dell'indice cluster [Appointment]. Tutte le righe verranno scansionate da questa tabella, ma solo quelle corrispondenti al filtro attivato [Start]verranno restituite all'operatore di join. All'interno dell'operatore di join i risultati vengono ulteriormente ridotti.

Il piano di query sopra descritto può andare bene se in realtà c'è solo una riga inviata all'input esterno del join. Tuttavia, se la stima della cardinalità dal join è errata e otteniamo, diciamo, 1000 righe, allora SQL Server eseguirà 1000 scansioni dell'indice in cluster [Appointment]. Le prestazioni del piano di query sono molto sensibili ai problemi di stima.

Il modo più diretto per non recuperare più quel piano di query sarebbe quello di creare un indice di copertura contro la [Appointment]tabella. Qualcosa come un indice su [ProjectId]e [Start]dovrebbe farlo. Sembra che questo sia esattamente l' [idx_appointment_start]indice che hai creato per risolvere il problema. Un altro modo per scoraggiare SQL Server dalla scelta del piano di query consiste nel correggere la stima della cardinalità dal join su [Permission]e [Project]. I modi tipici per farlo includono la modifica del codice, l'aggiornamento delle statistiche, l'utilizzo del CE legacy, la creazione di statistiche multi-colonna, la fornitura di più informazioni su SQL Server su variabili locali come un RECOMPILEsuggerimento o la materializzazione di tali righe in una tabella temporanea. Molte di queste tecniche non sono un buon approccio quando hai bisogno di tempi di risposta a livello di ms o devi scrivere codice attraverso un ORM.

L'indice creato [AppointmentAttendee]non è un modo diretto per risolvere il problema. Tuttavia, otterrai statistiche multi-colonna sull'indice e tali statistiche potrebbero scoraggiare il piano di query errato. L'indice può fornire un modo più efficiente per accedere ai dati che può anche scoraggiare il piano di query errato, ma non credo che ci sia alcun tipo di garanzia che non accadrà di nuovo solo con l'indice attivo [AppointmentAttendee].

3. Come posso assicurarmi che ciò non accada a un'altra query / piano?

Capisco perché stai ponendo questa domanda, ma è estremamente ampia. Il mio unico consiglio è di cercare di comprendere meglio la causa principale dell'instabilità del piano di query, di convalidare la creazione degli indici giusti per il carico di lavoro e di testare e monitorare attentamente il carico di lavoro. Microsoft offre alcuni consigli generali su come gestire le regressioni del piano di query causate dal nuovo CE in SQL Server 2016:

Il flusso di lavoro consigliato per l'aggiornamento di Query Processor all'ultima versione del codice è:

  1. Aggiorna un database a SQL Server 2016 senza modificare il livello di compatibilità del database (mantienilo al livello precedente)

  2. Abilitare l'archivio query sul database. Per ulteriori informazioni sull'abilitazione e l'utilizzo dell'archivio query, consultare Monitoraggio delle prestazioni mediante l'archivio query.

  3. Attendere un tempo sufficiente per raccogliere dati rappresentativi del carico di lavoro.

  4. Modificare il livello di compatibilità del database su 130

  5. Utilizzando SQL Server Management Studio, valutare se vi sono regressioni delle prestazioni su query specifiche dopo la modifica del livello di compatibilità

  6. Per i casi in cui vi sono regressioni, forzare il piano precedente nell'archivio query.

  7. Se esistono piani di query che non vengono forzati o se le prestazioni sono ancora insufficienti, prendere in considerazione il ripristino del livello di compatibilità all'impostazione precedente e quindi il supporto del supporto clienti Microsoft.

Non sto dicendo che è necessario eseguire il downgrade a SQL Server 2012 e ricominciare da capo, ma la tecnica generale descritta può essere utile per te.

2. Dovrei "forzare" il piano che funziona bene ora?

Dipende interamente da te. Se ritieni di avere un piano di query che funzioni bene per tutti i possibili parametri di input, ti senti a tuo agio con la funzionalità dell'archivio di query e desideri la tranquillità che deriva dalla forzatura di un piano di query, quindi provalo. Dopo tutto, forzare i piani di query che presentavano regressioni fa parte dei criteri di aggiornamento di Microsoft a SQL Server 2016.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.