Query a esecuzione prolungata su replica di sola lettura che richiede momenti sul primario


8

Ho una configurazione AG a 4 nodi come segue:

Configurazione hardware VM di tutti i nodi:

  • Microsoft SQL Server 2017 Enterprise Edition (RTM-CU14) (KB4484710)
  • 16 vCPU
  • 356 GB di RAM (lunga storia a questo ...)
  • grado massimo di parallelismo: 1 (come richiesto dal fornitore dell'app)
  • soglia di costo per il parallelismo: 50
  • memoria server massima (MB): 338944 (331 GB)

Configurazione AG:

  • Nodo 1: Committente primario o sincrono Secondario non leggibile, configurato per il failover automatico
  • Nodo 2: Commissione primaria o sincrona Secondario non leggibile, configurato per il failover automatico
  • Nodo 3: set secondario leggibile con commit asincrono, configurato per il failover manuale
  • Nodo 4: set secondario leggibile con commit asincrono, configurato per il failover manuale

La domanda in questione:

Non c'è nulla di terribilmente pazzo in questa query, fornisce un riepilogo degli elementi di lavoro in sospeso in varie code all'interno dell'applicazione. È possibile visualizzare il codice da uno dei collegamenti del piano di esecuzione di seguito.

Comportamento di esecuzione sul nodo primario:

Quando eseguito sul nodo primario, il tempo di esecuzione è generalmente intorno al segno di 1 secondo. Ecco il piano di esecuzione e di seguito sono riportate le statistiche acquisite da STATISTICS IO e STATISTICS TIME dal nodo primario:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 500 ms,  elapsed time = 656 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Comportamento di esecuzione sul nodo secondario di sola lettura:

Quando si esegue su un nodo secondario di sola lettura (ad esempio nodo 3 o nodo 4), questa query utilizza lo stesso piano di esecuzione (si tratta di un collegamento di piano diverso) e approssimativamente vengono visualizzate le stesse statistiche di esecuzione (ad esempio, potrebbero esserci alcune pagine in più esegue la scansione in quanto questi risultati cambiano sempre), ma ad eccezione del tempo della CPU, sembrano molto simili. Ecco le statistiche acquisite da STATISTICS IO e STATISTICS TIME dal nodo secondario di sola lettura:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 55719 ms,  elapsed time = 56335 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Altri dettagli:

Ho anche eseguito entrambi sp_WhoIsActivee loWaitingTasks.sql script di Paul Randal sul secondario mentre questa query è in esecuzione, ma non vedo attese che si verifichino, il che è francamente frustrante:

inserisci qui la descrizione dell'immagine

Anche questo non sembra essere un caso di latenza AG poiché lo stato di sincronizzazione è in realtà abbastanza buono:

--https://sqlperformance.com/2015/08/monitoring/availability-group-replica-sync

SELECT 
       ar.replica_server_name, 
       adc.database_name, 
       ag.name AS ag_name, 
       drs.is_local, 
       drs.synchronization_state_desc, 
       drs.synchronization_health_desc, 
       --drs.last_hardened_lsn, 
       --drs.last_hardened_time, 
       drs.last_redone_time, 
       drs.redo_queue_size, 
       drs.redo_rate, 
       (drs.redo_queue_size / drs.redo_rate) / 60.0 AS est_redo_completion_time_min,
       drs.last_commit_lsn, 
       drs.last_commit_time
FROM sys.dm_hadr_database_replica_states AS drs
INNER JOIN sys.availability_databases_cluster AS adc 
       ON drs.group_id = adc.group_id AND 
       drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag
       ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar 
       ON drs.group_id = ar.group_id AND 
       drs.replica_id = ar.replica_id
ORDER BY 
       ag.name, 
       ar.replica_server_name, 
       adc.database_name;

inserisci qui la descrizione dell'immagine

Questa query sembra essere il peggior trasgressore. Altre query che richiedono anche sub-secondi sul nodo primario possono richiedere da 1 a 5 secondi sul nodo secondario e, sebbene il comportamento non sia così grave, sembra causare problemi.

Infine, ho anche esaminato i server e verificato la presenza di processi esterni come scansioni A / V, lavori esterni che generano I / O imprevisti, ecc. E sono usciti a mani vuote. Non credo che ciò sia causato da qualcosa al di fuori del processo di SQL Server.

La domanda:

Sono solo mezzogiorno dove sono ed è già stata una lunga giornata, quindi sospetto che qui manchi qualcosa di ovvio. O quello o abbiamo qualcosa di configurato male, il che è possibile in quanto abbiamo avuto un numero di chiamate verso il fornitore e MS relative a questo ambiente.

Per tutte le mie indagini, non riesco proprio a trovare ciò che sta causando questa differenza nelle prestazioni. Mi aspetterei di vedere una sorta di attesa sui nodi secondari, ma nulla. Come posso risolvere ulteriormente questo problema per identificare la causa principale? Qualcuno ha già visto questo comportamento e ha trovato un modo per risolverlo?

AGGIORNAMENTO # 1 Dopo aver scambiato gli stati del terzo nodo (una delle repliche di sola lettura) in non leggibili e quindi di nuovo leggibili come test, quella replica viene ancora trattenuta da una transazione aperta, con tutte le query client che visualizzano il HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGaspettare.

L'esecuzione di un DBCC OPENTRANcomando produce i seguenti risultati:

Oldest active transaction:
    SPID (server process ID): 420s
    UID (user ID) : -1
    Name          : QDS nested transaction
    LSN           : (941189:33148:8)
    Start time    : May  7 2019 12:54:06:753PM
    SID           : 0x0
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Quando si cerca questo SPID sp_who2, lo mostra come un BACKGROUNDprocesso con QUERY STORE BACKelencato come comando.

Mentre noi siamo in grado di prendere i backup TLOG, ho il sospetto che ci sono in esecuzione in simili funzionalità di questo bug risolto , così ho intenzione di aprire un biglietto con MS su questo particolare problema di oggi.

A seconda del risultato di quel biglietto, cercherò di catturare una traccia dello stack di chiamate su suggerimento di Joe e vedere dove andiamo.

Aggiornamento finale (problema risolto automaticamente)

Dopo aver eclissato il segno di 52 ore della transazione del Query Store aperta (come identificato sopra), l'AG ha deciso di eseguire automaticamente il failover. Prima che ciò accadesse, ho fatto alcune metriche aggiuntive. Per questo link , fornito da Sean, il database in questione aveva un archivio di versioni molto grande dedicato a questo database, in particolare a un certo punto avevo registrato 1651360 pagine sul reserved_page_countcampo e 13210880 per il reserved_space_kbvalore.

Secondo gli ERRORLOG, il failover si è verificato dopo un diluvio di 5 minuti di errori di hardening relativi QDS base transactione relativi alle QDS nested transactiontransazioni.

Il failover ha causato un'interruzione di circa 10 minuti nel mio caso. Il database ha una dimensione di ~ 6 TB ed è molto attivo, quindi secondo me era piuttosto buono. Mentre il nuovo nodo primario era online durante questo periodo, non è stato possibile completare alcuna query del client poiché erano tutti in attesa sul QDS_LOADDBtipo di attesa.

Dopo il failover, i numeri dell'archivio versione sono stati ridotti a 176 per reserved_page_counte 1408 per reserved_space_kb. Anche le query sulle repliche secondarie di sola lettura hanno iniziato a essere eseguite rapidamente come se fossero eseguite dal primario, quindi sembra che il comportamento sia scomparso del tutto, a seguito del failover.


Se non è possibile modificare la lunghezza delle transazioni aperte sul primario o controllare le query con risposta pesante sul secondario, quindi il puntamento del carico di lavoro sul primario dovrebbe alleviare il problema di lunga durata, sebbene potrebbe colpire altri tipici problemi relativi alle query. Non direi che è normale impostare repliche come illeggibili per chiarire le cose, ma è una buona tecnica di risoluzione dei problemi. Tutto dipende se puoi / vuoi correggere la causa sottostante o solo il sintomo quando le cose peggiorano.
Sean Gallardy - Utente in pensione

1
Ehi, John - ottimo follow-up con questa domanda. Volevo solo menzionare QDS_LOADDB: se vuoi evitarlo in futuro, ma mantieni comunque il Query Store attivo, puoi usare questi flag di traccia raccomandati da Microsoft. In particolare 7752 consentirà l'esecuzione delle query prima dell'inizializzazione dell'archivio query (quindi potresti perdere alcune query, ma il tuo database sarà attivo).
Josh Darnell,

John: hai qualche possibilità che il tuo carico di lavoro non sia parametrizzato, scarsamente parametrizzato o altamente ad hoc? Ho riscontrato alcuni problemi con QDS relativi ai carichi di lavoro di tipo ad hoc
AM, due

@AMtwo Sì, la maggior parte delle query che colpiscono il database sono generate sul client e non sono parametrizzate (es. Query ad hoc).
John Eisbrener,

La bandiera della traccia @JoshDarnell 7752sembra particolarmente utile. Grazie per il consiglio!
John Eisbrener,

Risposte:


9

Questa risposta si aggiunge alla risposta di Joe in quanto non posso essere sicuro al 100% che si tratti del negozio di versioni, tuttavia finora ci sono prove sufficienti per suggerire di far parte del problema.

Quando una replica secondaria viene contrassegnata come leggibile, è necessario innanzitutto ottenere un buono stato costante per le informazioni sul controllo delle versioni in modo che sia presente un punto di partenza noto e valido per tutte le operazioni di lettura sul secondario. Quando questo è in attesa di transizione e ci sono ancora transazioni aperte sul primario, questo si manifesterà come HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGed è anche un buon indicatore che il primario subisce un bel po 'di sfornamento di dati (o almeno qualcuno ha una transazione aperta davvero lunga che inoltre non va bene). Più sono aperte le transazioni e più sono presenti modifiche ai dati, maggiore sarà il controllo delle versioni.

Le repliche secondarie raggiungono lo stato leggibile utilizzando l'isolamento dello snapshot sotto le copertine per la sessione, anche se se si controllano le informazioni sulla sessione si vedrà apparire nella lettura predefinita impegnata. Poiché l'isolamento delle snapshot è ottimistico e utilizza l'archivio versioni, tutte le modifiche dovranno essere controllate. Ciò viene esacerbato quando vi sono molte query in esecuzione (e potenzialmente di lunga durata) sul secondario mentre la quantità di dati è elevata sul primario. Generalmente questo si manifesta solo in alcune tabelle per un sistema OLTP, ma dipende completamente dall'applicazione e dal carico di lavoro.

L'archivio versioni stesso viene misurato in generazioni e quando viene eseguita una query che richiede l'utilizzo dell'archivio versioni, il puntatore al record di controllo delle versioni viene utilizzato per puntare alla catena TempDB di quella riga. Dico catena, perché è un elenco di versioni per quella riga e l'intera catena deve essere percorsa in sequenza per trovare la versione corretta in base al timestamp iniziale della transazione in modo che i risultati siano in linea con i dati in quel determinato momento.

Se l'archivio versione ha molte generazioni per queste righe a causa di transazioni di lunga durata sulle repliche primarie e secondarie, ciò comporterà tempi più lunghi della media per l'esecuzione delle query e generalmente sotto forma di CPU superiore mentre tutti gli altri elementi apparentemente rimangono esattamente gli stessi - come piano di esecuzione, statistiche, righe restituite, ecc. La camminata della catena è quasi un'operazione di cpu, quindi quando le catene diventano molto lunghe e la quantità di righe restituite è molto alta, si ottiene un (non lineare, ma può essere vicino) aumentare il tempo per la query.

L'unica cosa che si può fare è limitare la lunghezza delle transazioni sia sul primario che sul secondario per assicurarsi che l'archivio delle versioni non diventi troppo grande in TempDB pur avendo molte generazioni. I tentativi di ripulire l'archivio delle versioni avvengono all'incirca una volta al minuto, tuttavia la pulizia richiede che tutte le versioni della stessa generazione non siano più necessarie prima che possano essere rimosse e tutte le versioni future non possano essere pulite fino a quando non è più necessaria la versione più vecchia. Pertanto, una query di lunga durata può causare l'impossibilità di ripulire efficacemente molte generazioni inutilizzate.

L'attivazione e la disattivazione della modalità di lettura della replica cancellano anche l'archivio delle versioni in quanto non è più leggibile.

Ci sono altri elementi che potrebbero anche essere in gioco, ma questo sembra il più plausibile dati i dati attuali e il modo in cui la replica stava reagendo.

DMV Versioning TempDB (da non confondere con il versioning ADR).


Quando si esegue una query contro sys.dm_tran_version_store_space_usage, restituisce 1651360 come my riservato_page_count e 13210880 per il mio valore riservato_spazio_kb per il database in questione. Le indicazioni sembrano buone ma hai identificato questo problema. Grazie ancora per la spiegazione dettagliata!
John Eisbrener,

1
Sono sicuro circa il 103% che hai chiamato correttamente il problema. Ho aggiornato la domanda con alcuni aggiornamenti, ma grazie mille per le tue opinioni!
John Eisbrener,

8

Dichiarazione di non responsabilità: non so nulla dei gruppi di disponibilità, ma conosco un po 'la risoluzione dei problemi relativi alle query che sembrano usare più CPU di quanto dovrebbero.

Hai un problema con la CPU in quanto ne stai usando troppo. Una cosa importante da dire sulle attese è che quasi tutte non sono occupate dalla CPU. Quando un lavoratore entra in uno stato di attesa ha ceduto e non è più in esecuzione sullo scheduler in SQLOS. Quindi, se si dispone di una query MAXDOP 1 con le seguenti statistiche di esecuzione:

Tempo CPU = 55719 ms, tempo trascorso = 56335 ms.

Hai raggiunto quasi il 99% di utilizzo della CPU per la query. Perché dovrebbero esserci statistiche di attesa significative per quella query? Potresti vederne alcuni se hai alcune attese occupate della CPU come attese esterne o preventive, ma neanche questo è garantito. La linea di fondo è che le statistiche di attesa potrebbero non essere così utili qui.

Ci sono alcune cose da controllare in ordine approssimativo (l'ordine dipende da ciò che sai sull'ambiente):

  • Il server secondario ha un monitoraggio costoso in corso come eventi estesi, tracce o profiling?
  • L'hardware del server secondario corrisponde approssimativamente a quello primario?
  • Ci sono problemi di configurazione o software con il server secondario?
  • Attese o chiusure significative? Potrebbe non essere applicabile alla tua query ma potrebbe comunque fornire indizi.
  • Qualche spinlock significativo?
  • Esistono altri DMV o cose che è possibile controllare in SQL Server che potrebbero fornire indizi? Lei ha affermato che i gruppi di disponibilità sono probabilmente una parte fondamentale del problema.
  • Cosa ti dice la traccia ETW?
  • Che tipo di accordi di supporto hai?

La maggior parte di quanto sopra è ben coperto di vari post di blog e documentazione, ma espanderò sulla traccia ETW. Se vuoi sapere perché SQL Server utilizza così tanta CPU per una determinata query e hai accesso all'host, puoi sempre eseguire la traccia ETW per visualizzare i callstack e vedere quanta CPU stanno facendo i vari callstack. In altre parole, il sistema operativo host è felice di dirti per quale CPU viene utilizzata se sai come chiedere. I metodi più comuni per eseguire la traccia ETW includono Windows Performance Recorder e PerfView .

Comprendere questi risultati richiede una profonda conoscenza interna ed è facile arrivare a una conclusione sbagliata. In molti casi è meglio raccogliere i dati grezzi e chiedere agli esperti di esaminarli. Quando si esegue una traccia, si desidera che nel SQL Server venga eseguita la minima attività possibile. Di seguito sono riportate alcune risposte che utilizzano la traccia ETW per trarre conclusioni su SQL Server:

Ho il sospetto che nel tuo caso, se riesci a raccogliere i callstacks mentre la query di 45 secondi viene eseguita, otterrai alcuni indizi molto utili sulla natura del problema.


5

Dato che il problema si è risolto da solo, sono rimasto a speculare sulla sua causa (rima non intenzionale). Sulla base del post di Sean e del fatto che una transazione aperta del Query Store sembra essere stata la causa principale della mia maggiore dimensione del negozio di versioni (ad esempio la causa delle HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGattese), posso solo supporre che il Query Store abbia avuto una parte nel comportamento che era presentata. Questo database è più grande (~ 6 TB), abbastanza attivo e la maggior parte delle query che lo colpiscono sono generate sul client e non parametrizzate (ad esempio query ad hoc), quindi non credo che il Query Store si presti a fornire molto utile in questo scenario. Per questo motivo, disabiliteremo il Query Store in questo ambiente durante una futura finestra di manutenzione, dopodiché sospetto che non vedremo più questo comportamento.

Abbiamo aperto un ticket con Microsoft, ma i tempi non sono stati a nostro favore in quanto il problema è stato risolto prima che potessimo fare qualsiasi analisi dettagliata tramite una traccia PSSDIAG o simili. Spero che saranno in grado di fare alcuni test localizzati e replicare questo problema nel caso in cui si sia verificato un errore. Se vengono identificati ulteriori aggiornamenti su una risoluzione più permanente, sarò sicuro di aggiornare anche questa risposta.

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.