it-swarm.it

SQL: Cosa sta rallentando INSERT se non CPU o IO?

Abbiamo un database per un prodotto che è pesante per la scrittura. Abbiamo appena acquistato un nuovo server con un SSD per aiutare. Con nostra sorpresa, gli inserimenti non sono stati più veloci rispetto alla nostra vecchia macchina con una memoria molto più lenta. Durante il benchmarking abbiamo notato che il tasso IO esibito dal processo di SQL Server era molto basso.

Ad esempio, ho eseguito lo script trovato su questa pagina , tranne per il fatto che ho aggiunto BEGIN TRAN e COMMIT attorno al loop. Nel migliore dei casi ho visto l'utilizzo del disco raggiungere i 7 Mb/s, mentre la CPU ha toccato appena il 5%. Il server ha 64 GB installati e utilizza 10. Il tempo di esecuzione totale è stato di 2 minuti e 15 secondi per la prima chiamata fino a circa 1 minuto per le chiamate successive. Il database è in fase di recupero semplice ed era inattivo durante il test. Ho lasciato cadere il tavolo tra ogni chiamata.

Perché una sceneggiatura così semplice è così lenta? L'hardware non viene quasi mai utilizzato. Entrambi gli strumenti dedicati di benchmarking del disco e SQLIO indicano che l'SSD funziona correttamente con velocità superiori a 500 Mb/s sia in lettura che in scrittura. Capisco che le scritture casuali sono più lente delle scritture sequenziali, ma mi aspetterei che un semplice inserimento come questo, su una tabella senza indicizzazione in cluster, sia molto più veloce.

Alla fine il nostro scenario è molto più complesso, ma sento che devo prima capire un caso semplice. In breve, la nostra applicazione elimina i vecchi dati, quindi utilizza SqlBulkCopy per copiare nuovi dati nelle tabelle di gestione temporanea, esegue alcuni filtri e infine utilizza MERGE e/o INSERT INTO a seconda dei casi per copiare i dati nelle tabelle finali.

-> MODIFICA 1: ho seguito la procedura collegata da Martin Smith e ho ottenuto il seguente risultato:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

Trovo strano che NETWORK_IO richieda la maggior parte del tempo, considerando che non ci sono risultati da visualizzare e nessun dato da trasferire altrove che nei file SQL. Il tipo NETWORK_IO include tutti gli IO?

-> EDIT 2: ho creato un 20Gb RAM disco e montato un database da lì. Il miglior tempo che ho avuto sull'SSD è di 48 secondi, con RAM = disco è sceso a 37 secondi. NETWORK_IO è ancora l'attesa maggiore. La velocità massima di scrittura sul disco RAM era di circa 250 Mb/s mentre è in grado di eseguire più gigabyte al secondo. non usavo molta CPU, quindi cosa sta trattenendo SQL?

20
Djof

So che è una vecchia domanda, ma questo potrebbe ancora aiutare gli utenti ed è un problema che si presenta di tanto in tanto.

Il motivo principale per cui stai raggiungendo un limite di prestazioni senza vedere alcun collo di bottiglia delle risorse è perché hai raggiunto il limite di ciò che è possibile elaborare in un singolo thread di sessione. Il ciclo non viene elaborato in parallelo, ma tutti gli inserti vengono eseguiti in serie.

Nel mio caso, ci vogliono 36 secondi per inserire 3 milioni di righe. Ciò significa 36/30000000 = 0,000012 secondi per riga. È abbastanza veloce. Sul mio sistema, sono sufficienti 0,000012 per eseguire tutti i passaggi necessari.

L'unico modo per farlo più velocemente è avviare una seconda sessione in parallelo.

Se inizio 2 sessioni in parallelo, eseguo entrambe 15 milioni di inserimenti. Entrambi finiscono in 18 secondi. Potrei ridimensionare di più, ma la mia configurazione di test attuale sta raggiungendo il 95% di CPU con due sessioni parallele, quindi fare 3 distorcerebbe i risultati poiché colpirei un collo di bottiglia della CPU.

Se inizio 2 sessioni parallele inserendo entrambe 3 milioni di righe, entrambe terminano in 39 secondi. quindi ora sono 6 milioni di righe in 39 secondi.

Ok, questo ci lascia ancora con l'attesa NETWORK_IO di presentarci.

Le attese NETWORK_IO vengono aggiunte dal fatto che si stanno utilizzando eventi estesi per rintracciarli. Nel mio caso l'inserto impiega 36 secondi (su avg). Quando si utilizza il modo evento esteso (dal collegamento sopra nel primo commento) questo è ciò che è registrato:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

Puoi vedere che sono registrati 68 secondi di NETWORK_IO. Ma poiché il ciclo di inserimento è una singola azione con thread che ha richiesto 36 secondi, questo non può essere. (Sì, vengono utilizzati più thread, ma le operazioni sono seriali, mai in parallelo, quindi non è possibile accumulare più tempo di attesa rispetto alla durata totale della query)

Se non utilizzo eventi estesi ma solo i DMV delle statistiche di attesa in un'istanza silenziosa (con solo io che eseguo l'inserimento) ottengo questo:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

Quindi NETWORK_IO che stavi vedendo nel registro degli eventi estesi non era correlato al tuo ciclo di inserimento. (Se non accendessi nocount, avresti un'enorme rete asincrona IO aspetta, +1 Martin)

Tuttavia, non so perché NETWORK_IO venga visualizzato nella traccia eventi estesa. Sicuramente la scrittura su un target di file asincrono degli eventi accumula ASYNC_NETWORK_IO, ma sicuramente questo viene fatto su un SPID diverso da quello su cui stiamo filtrando. Potrei farmi questa domanda come una nuova domanda)

10
Edward Dortland

In genere si inizia guardando sys.dm_exec_requests , in particolare al wait_time, wait_type e wait_resource per le tue richieste INSERT. Ciò fornirà una chiara indicazione di ciò che sta bloccando INSERT. I risultati indicheranno se si tratta di contesa di blocco, eventi di crescita dei file, attese di svuotamento del registro, contesa di allocazione (si manifesta come contesa di blocco della pagina PFS) ecc ecc. Dopo aver misurato, aggiornare la domanda di conseguenza. Vi esorto caldamente a fermarvi ora e leggere la metodologia di risoluzione dei problemi Waits and Queues prima di procedere.

9
Remus Rusanu

Ho eseguito lo script di test nella pagina collegata nell'OP con BEGIN TRAN/COMMIT attorno al loop. Sulla mia macchina, ci sono voluti 1:28 per completare la prima volta.

Quindi ho spostato questi due comandi fuori dal ciclo:

SELECT @Random = ROUND(((@Upper - @Lower -1) * Rand() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

Si è completato dopo 28 secondi.

Non so per certo cosa stia succedendo, ma suppongo che potrebbe esserci un sonno di qualche tipo nel codice Rand(), forse come parte dell'algoritmo che stanno usando per generare entropia (meglio casuale numeri).

FWIW, gli SSD non sono sempre la migliore tecnologia per le app pesanti. Per prestazioni ottimali, assicurarsi che il registro del DB si trovi su una lettera di unità diversa dai dati del DB, che il file di registro sia pre-cresciuto alla sua dimensione massima e non troncare mai il registro.

3
RickNZ

Un altro DMV che utilizzo per identificare la lentezza è sys.dm_os_waiting_tasks . Se la tua query non richiede molta CPU, puoi trovare ulteriori informazioni sulle attese da questo DMV.

1
StanleyJohns

Sto controllando l'elenco degli eventi di attesa per sql 2008 e non vedo NETWORK_IO elencato: http://technet.Microsoft.com/en-us/library/ms179984 (v = sql.100). aspx

Pensavo che NETWORK_IO fosse appena elencato come ASYNC_NETWORK_IO, quindi volevo chiedere se potevi controllare di nuovo la tua versione di SQL, perché sono semplicemente curioso di sapere come/perché quell'evento di attesa appare per quella versione.

Per quanto riguarda l'attesa della rete, sì, può succedere anche se stai lavorando su un server autonomo. Hai controllato le impostazioni per le tue schede di rete? Mi chiedo se sono un problema.

Alla fine della giornata ci sono solo alcuni colli di bottiglia delle risorse: memoria, CPU, I/O del disco, rete e blocco. Hai indicato che CPU e I/O non sono il problema e che hai un evento di attesa di NETWORK_IO, quindi ti suggerisco di esaminare prima quelle NIC schede.

0
SQLRockstar