Perché una query SELECT dovrebbe causare scritture?


34

Ho notato che su un server che esegue SQL Server 2016 SP1 CU6 a volte una sessione di eventi estesi mostra una query SELECT che causa scritture. Per esempio:

inserisci qui la descrizione dell'immagine

Il piano di esecuzione non mostra alcuna causa ovvia per le scritture, come una tabella hash, uno spool o un ordinamento che potrebbero essere riversati su TempDB:

inserisci qui la descrizione dell'immagine

Anche l'assegnazione di variabili a un tipo MAX o un aggiornamento automatico delle statistiche potrebbe causare questo, ma in questo caso non è stata la causa delle scritture.

Da cos'altro potrebbero essere le scritture?

Risposte:


8

maldestro

Non riuscivo a ricordare se li includessi nella mia risposta originale , quindi ecco un'altra coppia.

Bobine!

SQL Server ha molti spool diversi, che sono strutture di dati temporanee archiviate in tempdb. Due esempi sono i rocchetti Table e Index.

Quando si verificano in un piano di query, le scritture in tali spool saranno associate alla query.

NOCCIOLINE

Questi saranno anche registrati come scritture in DMV, profiler, XE, ecc.

Bobina indice

NOCCIOLINE

Rocchetto da tavolo

NOCCIOLINE

La quantità di scritture eseguite aumenterà con la dimensione dei dati inviati, ovviamente.

Fuoriuscite

Quando SQL Server non dispone di memoria sufficiente per determinati operatori, è possibile che alcune pagine vengano riversate sul disco. Ciò accade principalmente con specie e hash. Puoi vederlo nei piani di esecuzione effettivi e nelle versioni più recenti di SQL Server, anche gli sversamenti vengono tracciati in dm_exec_query_stats .

SELECT deqs.sql_handle,
       deqs.total_spills,
       deqs.last_spills,
       deqs.min_spills,
       deqs.max_spills
FROM sys.dm_exec_query_stats AS deqs
WHERE deqs.min_spills > 0;

NOCCIOLINE

NOCCIOLINE

puntamento

Puoi usare una sessione XE simile a quella che ho usato sopra per vederli nelle tue demo.

CREATE EVENT SESSION spools_and_spills
    ON SERVER
    ADD EVENT sqlserver.sql_batch_completed
    ( ACTION ( sqlserver.sql_text ))
    ADD TARGET package0.event_file
    ( SET filename = N'c:\temp\spools_and_spills' )
    WITH ( MAX_MEMORY = 4096KB,
           EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
           MAX_DISPATCH_LATENCY = 1 SECONDS,
           MAX_EVENT_SIZE = 0KB,
           MEMORY_PARTITION_MODE = NONE,
           TRACK_CAUSALITY = OFF,
           STARTUP_STATE = OFF );
GO

38

In alcuni casi, Query Store può causare scritture come effetto di un'istruzione select e nella stessa sessione.

Questo può essere riprodotto come segue:

USE master;
GO
CREATE DATABASE [Foo];
ALTER DATABASE [Foo] SET QUERY_STORE (OPERATION_MODE = READ_WRITE, 
  CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30), 
  DATA_FLUSH_INTERVAL_SECONDS = 900, 
  INTERVAL_LENGTH_MINUTES = 60, 
  MAX_STORAGE_SIZE_MB = 100, 
  QUERY_CAPTURE_MODE = ALL, 
  SIZE_BASED_CLEANUP_MODE = AUTO);
USE Foo;
CREATE TABLE Test (a int, b nvarchar(max));
INSERT INTO Test SELECT 1, 'string';

Creare una sessione Eventi estesi per il monitoraggio:

CREATE EVENT SESSION [Foo] ON SERVER 
ADD EVENT sqlserver.rpc_completed(SET collect_data_stream=(1)
    ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
    WHERE ([writes]>(0))),
ADD EVENT sqlserver.sql_batch_completed(SET collect_batch_text=(1)
    ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
    WHERE ([writes]>(0)))
ADD TARGET package0.event_file(SET filename=N'C:\temp\FooActivity2016.xel',max_file_size=(11),max_rollover_files=(999999))
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_MULTIPLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF);

Quindi eseguire quanto segue:

WHILE @@TRANCOUNT > 0 COMMIT
SET IMPLICIT_TRANSACTIONS ON;
SET NOCOUNT ON;
GO
DECLARE @b nvarchar(max);
SELECT @b = b FROM dbo.Test WHERE a = 1;
WAITFOR DELAY '00:00:01.000';
GO 86400

Una transazione implicita può o non può essere necessaria per riprodurla.

Per impostazione predefinita, all'inizio dell'ora successiva il processo di raccolta delle statistiche del Query Store scriverà i dati. Questo sembra (a volte?) Verificarsi come parte della prima query dell'utente eseguita durante l'ora. La sessione Eventi estesi mostrerà qualcosa di simile al seguente:

inserisci qui la descrizione dell'immagine

Il registro delle transazioni mostra le scritture che si sono verificate:

USE Foo;
SELECT [Transaction ID], [Begin Time], SPID, Operation, 
  [Description], [Page ID], [Slot ID], [Parent Transaction ID] 
FROM sys.fn_dblog(null,null) 
/* Adjust based on contents of your transaction log */
WHERE [Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
OR [Parent Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
ORDER BY [Current LSN];

inserisci qui la descrizione dell'immagine

Ispezionare la pagina con DBCC PAGEmostra che le scritture devono sys.plan_persist_runtime_stats_interval.

USE Foo;
DBCC TRACEON(3604); 
DBCC PAGE(5,1,344,1); SELECT
OBJECT_NAME(229575856);

Si noti che le voci del registro mostrano tre transazioni nidificate ma solo due record di commit. In una situazione simile nella produzione, ciò ha portato a una libreria client probabilmente difettosa che ha utilizzato transazioni implicite inaspettatamente avviando una transazione di scrittura, impedendo la cancellazione del registro delle transazioni. La libreria è stata scritta per emettere un commit solo dopo aver eseguito un'istruzione di aggiornamento, inserimento o eliminazione, quindi non ha mai emesso un comando di commit e lasciato aperta una transazione di scrittura.


25

C'è un'altra volta in cui ciò può accadere, e questo avviene con un aggiornamento automatico delle statistiche.

Ecco la sessione XE che vedremo:

CREATE EVENT SESSION batches_and_stats
    ON SERVER
    ADD EVENT sqlserver.auto_stats
    ( ACTION ( sqlserver.sql_text )),
    ADD EVENT sqlserver.sql_batch_completed
    ( ACTION ( sqlserver.sql_text ))
    ADD TARGET package0.event_file
    ( SET filename = N'c:\temp\batches_and_stats' )
    WITH ( MAX_MEMORY = 4096KB,
           EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
           MAX_DISPATCH_LATENCY = 30 SECONDS,
           MAX_EVENT_SIZE = 0KB,
           MEMORY_PARTITION_MODE = NONE,
           TRACK_CAUSALITY = OFF,
           STARTUP_STATE = OFF );
GO

Quindi useremo questo per raccogliere informazioni:

USE tempdb

DROP TABLE IF EXISTS dbo.SkewedUp

CREATE TABLE dbo.SkewedUp (Id INT NOT NULL, INDEX cx_su CLUSTERED (Id))

INSERT dbo.SkewedUp WITH ( TABLOCK ) ( Id )
SELECT CASE WHEN x.r % 15 = 0 THEN 1
            WHEN x.r % 5 = 0 THEN 1000
            WHEN x.r % 3 = 0 THEN 10000
            ELSE 100000
       END AS Id
FROM   (   SELECT     TOP 1000000 ROW_NUMBER() OVER ( ORDER BY @@DBTS ) AS r
           FROM       sys.messages AS m
           CROSS JOIN sys.messages AS m2 ) AS x;


ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = START

SELECT su.Id, COUNT(*) AS records
FROM dbo.SkewedUp AS su
WHERE su.Id > 0
GROUP BY su.Id

ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = STOP

Alcuni dei risultati interessanti della sessione XE:

NOCCIOLINE

L'aggiornamento automatico delle statistiche non mostra alcuna scrittura, ma la query mostra una scrittura immediatamente dopo l'aggiornamento delle statistiche.

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.