Qu'est-ce que la colonne «lectures» de sys.dm_exec_sessions indique réellement?

10

Cela peut sembler une question très fondamentale, et ça devrait l'être. Cependant, en tant que fan de la méthode scientifique, j'aime créer une hypothèse, puis la tester pour voir si j'ai raison. Dans ce cas, j'essaie de mieux comprendre la sortie sys.dm_exec_sessions, et plus précisément, la colonne unique "lit".

La documentation en ligne de SQL Server spécifie plutôt sèchement ceci:

Nombre de lectures effectuées, par requêtes dans cette session, au cours de cette session. N'est pas annulable.

On pourrait supposer que cela indiquerait le nombre de pages lues sur le disque pour satisfaire les demandes émises par cette session depuis le début de la session. C'est l'hypothèse que je pensais tester.

La logical_readscolonne de cette même table est définie comme suit:

Nombre de lectures logiques effectuées sur la session. N'est pas annulable.

D'après mon expérience avec SQL Server, je pense que cette colonne reflète le nombre de pages qui ont été lues à la fois sur le disque et en mémoire . En d' autres termes, le nombre total de pages jamais lu par la session, peu importe où ces pages résident. Le différenciateur, ou proposition de valeur, d'avoir deux colonnes distinctes qui offrent des informations similaires semble être que l'on peut comprendre le rapport entre les pages lues sur le disque ( reads) et celles lues dans le cache de tampon ( logical_reads) pour une session spécifique.

Sur mon banc de test, j'ai créé une nouvelle base de données, créé une seule table avec un nombre connu de pages de données, puis lu cette table dans une nouvelle session. Ensuite, j'ai regardé sys.dm_exec_sessionspour voir ce que les colonnes readset ont logical_readsdit sur la session. À ce stade, je suis confus par les résultats. Peut-être que quelqu'un ici peut faire la lumière à ce sujet pour moi.

Le banc d'essai:

USE master;
IF EXISTS (SELECT 1
    FROM sys.databases d 
    WHERE d.name = 'TestReads')
BEGIN
    ALTER DATABASE TestReads SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE TestReads;
END
GO
CREATE DATABASE TestReads;
GO
ALTER DATABASE TestReads SET RECOVERY SIMPLE;
BACKUP DATABASE TestReads TO DISK = 'NUL:'; /* ensure we are in 
                                            simple recovery model */
GO

USE TestReads;
GO

/*
    create a table with 2 rows per page, for easy math!
*/
CREATE TABLE dbo.TestReads
(
    ID INT NOT NULL
        CONSTRAINT PK_TestReads
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData CHAR(4000) NOT NULL
);

/*
    insert 5000 pages of data
*/
INSERT INTO dbo.TestReads (SomeData)
SELECT TOP(10000) o1.name
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
ORDER BY o1.object_id
    , o2.object_id
    , o3.object_id;


/*
    Verify we have 5,000 pages of data, with 10,000 rows.
*/
SELECT o.name
    , p.rows
    , au.total_pages
    , au.used_pages
    , au.data_pages
FROM sys.partitions p
    INNER JOIN sys.objects o ON p.object_id = o.object_id 
    INNER JOIN sys.allocation_units au 
        ON p.hobt_id = au.container_id 
        AND (au.type = 1 or au.type = 0)
WHERE p.index_id = 1
    AND o.name = 'TestReads'
    AND o.type = 'U';

/*
    issue a checkpoint to ensure dirty pages are flushed to disk
*/
CHECKPOINT 30;
DBCC DROPCLEANBUFFERS;
DBCC FREESYSTEMCACHE ('ALL');
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;
GO

/*
    ensure we have no data cached in memory for the TestReads database
*/
USE master;
ALTER DATABASE TestReads SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE TestReads SET ONLINE;

SELECT DatabaseName = d.name
    , SchemaName = s.name
    , ObjectName = o.name
    , AllocatedMB = COUNT(1) * 8192E0 / 1048576
    , PagesInMemory = COUNT(1)
FROM sys.dm_os_buffer_descriptors dobd
    INNER JOIN sys.allocation_units au 
        ON dobd.allocation_unit_id = au.allocation_unit_id
    INNER JOIN sys.partitions p 
        ON au.container_id = p.hobt_id 
             AND (au.type = 1 OR au.type = 0)
    INNER JOIN sys.objects o ON p.object_id = o.object_id
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
    INNER JOIN sys.databases d 
        ON dobd.database_id = d.database_id
WHERE d.name = 'TestReads'
    AND o.name = 'TestReads'
    AND o.type = 'U'
GROUP BY d.name
    , s.name
    , o.name;

Le premier énoncé de sélection ci-dessus montre qu'en fait, le tableau comprend 10 000 lignes, avec 5 025 pages au total, 5 020 pages utilisées et 5 000 pages de données; exactement comme on pourrait s'y attendre:

entrez la description de l'image ici

La deuxième instruction select confirme que nous n'avons rien en mémoire pour la TestReadstable.

Dans une nouvelle session , nous effectuons la requête suivante, en prenant note de la session_id:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

Comme on pouvait s'y attendre, cela lit la table entière du disque dans la mémoire, comme indiqué dans la sortie de SET STATISTICS IO ON:

(10000 row(s) affected)
Table 'TestReads'. Scan count 1, logical reads 5020, physical reads 3, 
read-ahead reads 4998, lob logical reads 0, lob physical reads 0, lob 
read-ahead reads 0.

Dans une troisième session, nous inspectons sys.dm_exec_sessions:

SELECT des.session_id
    , des.reads
    , des.logical_reads
FROM sys.dm_exec_sessions des
WHERE des.session_id = 57; /* session_id from the 2nd (previous) session */

Je m'attends à voir au moins 5 000 sys.dm_exec_sessionsspectacles pour et . Hélas, je vois montre zéro. montre un nombre attendu de lectures quelque part au nord de 5 000 - il montre 5 020 dans mon test:readslogical_readsreadslogical_reads

entrez la description de l'image ici

Je sais que SQL Server a lu la TestReadstable entière en mémoire, grâce au sys_dm_os_buffer_descriptorsDMV:

USE TestReads;
GO
SELECT DatabaseName = d.name
    , SchemaName = s.name
    , ObjectName = o.name
    , AllocatedMB = COUNT(1) * 8192E0 / 1048576
    , PagesInMemory = COUNT(1)
FROM sys.dm_os_buffer_descriptors dobd
    INNER JOIN sys.allocation_units au 
        ON dobd.allocation_unit_id = au.allocation_unit_id
    INNER JOIN sys.partitions p 
        ON au.container_id = p.hobt_id 
            AND (au.type = 1 OR au.type = 0)
    INNER JOIN sys.objects o ON p.object_id = o.object_id
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
    INNER JOIN sys.databases d 
        ON dobd.database_id = d.database_id
WHERE d.name = 'TestReads'
    AND o.name = 'TestReads'
    AND o.type = 'U'
GROUP BY d.name
    , s.name
    , o.name;

entrez la description de l'image ici

Qu'est-ce que je fais mal?

J'utilise SQL Server 2012 11.0.5343 pour ce test.


Autres constatations:

Si je lance ce qui suit:

SELECT des.session_id
    , des.reads
    , des.logical_reads
FROM sys.dm_exec_sessions des

Je vois reads784 dans la session où je crée le banc d'essai; cependant, toutes les autres sessions affichent zéro dans la readscolonne.

J'ai maintenant mis à jour mon instance de test SQL Server vers 11.0.6020; cependant le résultat est le même.

Max Vernon
la source
sys.dm_exec_requestsvous donnera presque les mêmes set statistics io onrésultats.
Kin Shah
1
Intéressant SET STATISTICS IO ONjuste avant de lire le tableau de la 2e session rapporte 3 lectures physiques et 4998 lectures anticipées; mais sys.dm_exec_sessionsne reflète toujours pas cela dans la readscolonne.
Max Vernon
2
En 2012, je vois souvent 0 pour les lectures et les lectures logiques malgré les résultats non nuls rapportés par STATISTICS IO i.stack.imgur.com/XbHae.png
Martin Smith
1
En fait, je vois les deux colonnes zéro avec mon approche sur toutes les éditions que j'ai testées de 2008 à SQL2016CTP3
Martin Smith
1
@MartinSmith et Max: J'ai également vu un retard dans certains incréments des readschamps. Je soupçonne que cela fonctionne un peu comme session_space_usage ou tout autre DMV qui montre l'utilisation de tempdb par session qui n'augmente pas jusqu'à la fin de la "demande".
Solomon Rutzky

Réponses:

2

Ma compréhension a toujours été que ce readsn'est que physique (c'est-à-dire à partir du disque) et logical_readsuniquement à partir du pool de tampons (c'est-à-dire à partir de la mémoire). J'ai fait un test rapide avec un tableau plus petit qui n'a que 2 pages de données et 3 pages au total, et ce que je vois semble confirmer ces deux définitions.

Une chose qui vous donne probablement de mauvais résultats est que vous n'effacez pas la mémoire. Vous devez exécuter ce qui suit entre les tests pour le forcer à recharger à partir du disque:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

Ma configuration de test était simplement la suivante:

CREATE TABLE dbo.ReadTest (Col1 CHAR(7500) DEFAULT (' '));
INSERT INTO dbo.ReadTest (Col1) VALUES (DEFAULT), (DEFAULT);

J'ai ensuite exécuté ce qui suit:

SELECT reads, logical_reads FROM sys.dm_exec_sessions WHERE session_id = @@SPID;
SELECT * FROM dbo.ReadTest;

(Oui, je testais au cours de la même session que celle sur laquelle j'exécutais le DMV, mais cela n'a pas faussé les résultats pour le readsterrain, et si rien d'autre, était au moins cohérent s'il contribuait au logical_readsterrain.)

Pour tester, j'exécuterais la commande DBCC puis les deux requêtes SELECT. Ensuite, je verrais un saut dans les champs readset logical_reads. Je réexécutais les requêtes SELECT et parfois je voyais un saut supplémentaire reads.

Après cela, j'exécuterais les deux requêtes SELECT plusieurs fois et readsresterait la même tandis que la logical_readshausse de 4 à chaque fois.

Je recommencerais alors avec l'exécution du DBCC et verrais ce même modèle. Je l'ai fait plusieurs fois et les chiffres rapportés étaient cohérents sur toutes les séries de tests.


Plus d'informations:

Je teste également sur SQL Server 2012, SP2 - 64 bits (11.0.5343).

Les commandes DBCC suivantes, nous avons à la fois essayé et vu aucun effet:

DBCC FREESYSTEMCACHE('ALL');
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;

La plupart du temps, DBCC DROPCLEANBUFFERScela fonctionne, mais je constate parfois qu'il est toujours dans le pool de tampons. Impair.

Quand je:

  • DBCC DROPCLEANBUFFERS: Les lectures augmentent de 24 et les lectures_logiques augmentent de 52.
  • Réexécuter SELECT [Col1] FROM dbo.ReadTest;: les lectures n'augmentent pas, mais les lectures_logiques augmentent de 6.
  • Ajoutez un espace au texte de la requête et réexécutez: les lectures n'augmentent pas, mais les lectures logiques augmentent de 52 (tout comme juste après DBCC DROPCLEANBUFFERS).

Il semblerait que les 52 lectures logiques tiennent compte de la génération du plan et des résultats, ce qui implique que la génération du plan a provoqué les 46 lectures logiques supplémentaires. Mais les lectures physiques ne remontent pas et pourtant ce sont les mêmes 52 lectures logiques que c'était quand il fallait aussi faire les lectures physiques, donc logical_readsn'inclut pas les physiques reads. Je tiens simplement à préciser ce point, qu'il soit ou non énoncé ou sous-entendu dans la Question.

MAIS, un comportement que j'ai remarqué qui se déclenche (au moins un peu) en utilisant l'existence des pages de données de la table dans sys.dm_os_buffer_descriptors: il est rechargé par un autre processus. Si vous DROPCLEANBUFFERS et vérifiez immédiatement, alors il devrait avoir disparu. Mais attendez quelques minutes et cela réapparaît, mais cette fois sans toutes les pages de données. Dans mon test, le tableau a 1 page IAM et 4 pages de données. Les 5 pages sont dans le pool de tampons après avoir fait le SELECT. Mais quand il est rechargé par un autre processus, ce n'est que la page IAM et 1 page de données. Je pensais que ce pourrait être SSMS IntelliSense, mais j'ai supprimé toutes les références à ce nom d'objet dans mon onglet de requête et il est toujours rechargé.

Solomon Rutzky
la source
assez drôle, j'ai supprimé les DBCC DROPCLEANBUFFERS(et les autres DBCC DROPxxxcommandes) de mon banc de test car ils ne faisaient aucune différence. La mise hors ligne de la base de données supprime tous les tampons et tout le reste associé à la base de données.
Max Vernon
J'étais sous la même compréhension que vous au sujet des lectures étant physiques, et les lectures logiques commencent à partir du pool de tampons, btw.
Max Vernon
Je l'ai aussi essayé avec: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Max Vernon
1
@MaxVernon Pourrait être la fonction "keep 'em guessin" ;-)
Solomon Rutzky
2
@MaxVernon, n'oubliez pas d'exécuter un CHECKPOUNTdans le contexte de la base de données avant DBCC DROPCLEANBUFFERS.
Dan Guzman