web-dev-qa-db-fra.com

Qu'est-ce que la colonne "Reads" dans SYS.DM_EXEC_SESSESSIONS indique réellement?

Cela peut sembler une question très fondamentale, et même cela devrait être. Cependant, comme ventilateur de la méthode scientifique, j'aime créer une hypothèse, puis le tester pour voir si je suis correct. Dans ce cas, j'essaie de mieux comprendre la production de sys.dm_exec_sessions, et plus précisément, la colonne unique "lit".

Les livres SQL Server en ligne le spécifient plutôt schmiquement comme suit:

Nombre de lectures effectuées, selon les demandes de cette session, au cours de cette session. N'est pas nullable.

On pourrait supposer que cela indiquerait le nombre de pages Lire du 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.

Le logical_reads colonne dans la même table est définie comme suit:

Nombre de lectures logiques effectuées à la session. N'est pas nullable.

De l'expérience en utilisant SQL Server, je pense que cette colonne reflète le nombre de pages qui ont été lues à la fois à partir du disque et en mémoire . En d'autres termes, le nombre total de pages jamais Lisez par la session, peu importe où ces pages résident. Le différentiel, ou la proposition de valeur, d'avoir deux colonnes distinctes offrant des informations similaires. semble-t-il Pour que l'on puisse comprendre le rapport des pages lues à partir du disque (reads ) vs ceux lus dans le cache tampon (logical_reads) Pour une session spécifique.

Sur ma plate-forme d'essai, j'ai créé une nouvelle base de données créée une seule table avec un nombre connu de pages de données, puis lisez ce tableau dans une nouvelle session. Puis j'ai regardé sys.dm_exec_sessions Pour voir ce que la reads et logical_reads Colonnes a dit à propos de la session. À ce stade, je suis confondu par les résultats. Peut-être que quelqu'un ici peut éclairer cela pour moi pour moi.

La plate-forme 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;

La première instruction SELECT ci-dessus montre qu'en fait, la table est composée de 10 000 lignes, avec 5 025 pages totales, 5 020 pages utilisées et 5 000 pages de données; précisément comme on pourrait s'attendre:

enter image description here

La deuxième instruction SELECT confirme que nous n'avons rien en mémoire pour la table TestReads.

Dans un nouvelle session , nous faisons la requête suivante, prenant note de la session_id:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

Comme on pourrait s'y attendre, cela lit toute la table du disque en 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 un , troisième , 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'attendrais à voir sys.dm_exec_sessions show au moins 5 000 pour les deux reads et logical_reads. Hélas, je vois reads montre zéro. logical_reads montre un nombre attendu de lecture quelque part au nord de 5 000 - il montre 5 020 dans mon test:

enter image description here

Je sais que SQL Server lit toute la table TestReads dans la mémoire, en vertu du sys_dm_os_buffer_descriptors DMV:

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;

enter image description here

Qu'est-ce que je fais mal?

J'utilise SQL Server 2012 11.0.5343 pour ce test.


Autres résultats:

Si je cours ce qui suit:

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

Je vois reads de 784 à la session où je crée la plate-forme de test; Cependant, toutes les autres sessions montrent zéro dans la colonne reads.

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

10
Max Vernon

Ma compréhension a toujours été que reads n'est que physique (c'est-à-dire du disque) et logical_reads est seulement à partir du piscine tampon (c'est-à-dire de la mémoire). J'ai fait un test rapide avec une table plus petite 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, c'est que vous ne nettoyez pas la mémoire. Vous devez exécuter les suivants entre les tests pour la forcer à recharger à partir du disque:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

Ma configuration de test était la suivante:

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

J'ai ensuite couru ce qui suit:

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

(Oui, je testais dans la même session que j'exécutais le DMV dans, mais cela n'a pas rakew les résultats pour le champ reads, et si rien d'autre, n'était au moins cohérent s'il contribuait à les logical_reads domaine.)

Pour tester, j'exécuterais la commande DBCC, puis les deux requêtes SELECT. Ensuite, je verrais un saut dans les deux reads et logical_reads des champs. Je voudrais exécuter les requêtes Sélectionner à nouveau et parfois je verrais un saut supplémentaire dans reads.

Après cela, j'exécuterais les deux requêtes sélectionnées plusieurs fois et le reads resterait la même alors que le logical_reads monta 4 chaque fois.

Je commencerais ensuite à exécuter le DBCC et à voir ce même motif. Je l'ai fait plusieurs fois et les chiffres rapportés étaient conformes à tous les essais.


Plus d'informations:

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

Les commandes DBCC suivantes nous avons à la fois éprouvées et n'ont aucun effet:

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

Le plus souvent DBCC DROPCLEANBUFFERS fonctionne, mais je vois de temps en temps qu'il est toujours dans la piscine tampon. Impair.

Quand je:

  • DBCC DROPCLEANBUFFERS: Les lectures montent par 24 et logical_reads montent par 52.
  • Cours SELECT [Col1] FROM dbo.ReadTest; Encore une fois: les lectures ne montent pas, mais logical_reads montent par 6.
  • Ajoutez un espace au texte de la requête et re-exécuter: les lectures ne montent pas, mais les Logical_reads montent de 52 (juste comme juste après le DBCC DROPCLEANBUFFERS).

Il semblerait que les 52 représentent des comptes logiques pour la génération de plans et les résultats, ce qui implique que la génération de plan a provoqué les 46 lectures logiques supplémentaires. Mais les lectures physiques ne montaient pas à nouveau et pourtant, il est identique 52 lectures logiques que lorsqu'il avait besoin de faire les lectures physiques, donc logical_reads n'inclut pas le physique reads. Je suis juste en train de faire clairement clairement, qu'il soit indiqué ou implicite dans la question.

Mais, un comportement que j'ai constaté que je constate que, 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, cela devrait être parti. Mais attendez quelques minutes et cela se présente à nouveau, mais cette fois-ci sans toutes les pages de données. Dans mon test, la table contient 1 page IAM et 4 pages de données. Les 5 pages sont dans la piscine tampon après que je faisais le SELECT. Mais quand il est rechargé par un autre processus, c'est juste la page IAM et 1 page de données. Je pensais que cela pourrait être SSMS IntelliSense, mais j'ai supprimé toutes les références à ce nom d'objet dans mon onglet de requête et qu'il est toujours rechargé.

2
Solomon Rutzky