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_WhoIsActive
e loWaitingTasks.sql
script di Paul Randal sul secondario mentre questa query è in esecuzione, ma non vedo attese che si verifichino, il che è francamente frustrante:
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;
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_VERSIONING
aspettare.
L'esecuzione di un DBCC OPENTRAN
comando 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 BACKGROUND
processo con QUERY STORE BACK
elencato 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_count
campo e 13210880 per il reserved_space_kb
valore.
Secondo gli ERRORLOG, il failover si è verificato dopo un diluvio di 5 minuti di errori di hardening relativi QDS base transaction
e relativi alle QDS nested transaction
transazioni.
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_LOADDB
tipo di attesa.
Dopo il failover, i numeri dell'archivio versione sono stati ridotti a 176 per reserved_page_count
e 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.
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).
7752
sembra particolarmente utile. Grazie per il consiglio!