На що насправді вказує стовпець «читає» у sys.dm_exec_sesions?


10

Це може здатися дуже базовим питанням, і справді воно повинно бути. Однак, як шанувальник наукового методу, я люблю створювати гіпотезу, а потім перевірити її, щоб перевірити, чи я прав. У цьому випадку я намагаюся краще зрозуміти результат sys.dm_exec_sessions, а точніше, єдиний стовпець "читається".

SQL Server Books Online досить суворо визначає це як:

Кількість прочитаних запитів за запитами на цій сесії протягом цієї сесії. Не є нульовим.

Можна припустити, що це вказуватиме кількість сторінок, прочитаних з диска, щоб задовольнити запити, видані цим сеансом з початку сеансу. Це гіпотеза, яку я думав перевірити.

logical_readsСтовпець в тій же таблиці, визначається як:

Кількість логічних читань, які були виконані на сеансі. Не є нульовим.

З досвіду використання SQL Server, я вважаю, що цей стовпець відображає кількість сторінок, прочитаних як з диска, так і з пам'яті . Іншими словами, загальна кількість сторінок, які коли-небудь читав сеанс, незалежно від того, де вони перебувають. Диференціатор або пропозиція значення, що має два окремі стовпці, які пропонують подібну інформацію, здається, що можна зрозуміти співвідношення сторінок, прочитаних з диска ( reads), і тих, які читаються з кеш-пам'яті буфера ( logical_reads) для конкретного сеансу.

На своїй тестовій установці я створив нову базу даних, створив єдину таблицю з відомою кількістю сторінок даних, а потім прочитав цю таблицю в новому сеансі. Потім я подивився на , sys.dm_exec_sessionsщоб побачити , що readsі logical_readsстовпці говорилося про сесії. На даний момент я збентежений результатами. Можливо, хтось тут може пролити трохи світла на це на мене.

Випробувальна установка:

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;

Перше твердження про вибір вище показує, що насправді таблиця складається з 10 000 рядків із загальною кількістю 5025 сторінок, 5,020 сторінок, що використовуються, та 5000 сторінок даних; саме так, як можна було б очікувати:

введіть тут опис зображення

Другий оператор select підтверджує, що ми не маємо нічого в пам'яті для TestReadsтаблиці.

У новому сеансі ми робимо наступний запит, беручи до уваги session_id:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

Як і слід було очікувати, це зчитує всю таблицю з диска в пам'ять, як показано у висновку з 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.

На третій сесії ми перевіряємо 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 */

Я очікував би побачити sys.dm_exec_sessionsшоу принаймні 5000 для обох readsта logical_reads. На жаль, я бачу, readsпоказує нуль. logical_readsпоказує очікувану кількість прочитаних десь на північ від 5000 - це показує 5,020 у моєму тесті:

введіть тут опис зображення

Я знаю, що SQL Server читав всю TestReadsтаблицю в пам'ять завдяки 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;

введіть тут опис зображення

Що я роблю неправильно?

Я використовую SQL Server 2012 11.0.5343 для цього тесту.


Подальші результати:

Якщо я запускаю наступне:

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

Я бачу reads784 на сеансі, де я створюю тестову установку; однак усі інші сеанси показують нуль у readsстовпці.

Зараз я оновив тестовий екземпляр SQL Server до 11.0.6020; однак результат той самий.


sys.dm_exec_requestsдасть вам майже те саме, що і set statistics io onрезультати.
Кін Шах

1
Цікаво SET STATISTICS IO ONякраз перед тим, як я прочитав із таблиці в 2-му сеансі звіти про 3 фізичні читання, а 4998 - заздалегідь прочитані; однак це sys.dm_exec_sessionsвсе ще не відображає це в readsстовпці.
Макс Вернон

2
У 2012 році я часто бачу 0 як для читання, так і для логічного читання, незважаючи на ненульові результати, повідомлені з STATISTICS IO i.stack.imgur.com/XbHae.png
Мартін Сміт

1
Насправді я бачу, що обидва стовпці дорівнюють моєму підходу до всіх видань, які я перевірив з 2008 року до SQL2016CTP3
Мартін Сміт

1
@MartinSmith та Max: У мене також спостерігається затримка деяких приростів readsполів. Я підозрюю, що він працює так само, як session_space_usage або будь-який інший DMV, який показує використання tempdb за сеанс, який не збільшується, поки "запит" не завершиться.
Соломон Руцький

Відповіді:


2

Моє розуміння завжди було readsлише фізичним (тобто з диска) і logical_readsтільки з буферного пулу (тобто з пам'яті). Я зробив швидкий тест з меншою таблицею, яка містить лише 2 сторінки даних і 3 сторінки, і те, що я бачу, підтверджує ці два визначення.

Одне, що, ймовірно, дає вам погані результати - це те, що ви не очищаєте пам'ять. Ви повинні виконати наступне між тестами, щоб змусити його перезавантажити з диска:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

Моя тестова установка була лише такою:

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

Потім я запустив таке:

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

(Так, я тестував на тому самому сеансі, коли я запускав DMV, але це не перекосило результати для цього readsполя, і якщо нічого іншого, то було принаймні послідовним, якщо воно сприяло б logical_readsполе.)

Для тестування я запустив би команду DBCC, а потім два запити SELECT. Тоді я побачив би стрибок і в, readsі в logical_readsполях. Я б запустив SELECT запити ще раз, і інколи побачив би додатковий стрибок reads.

Після цього я б запускав два запити SELECT багато разів, і той readsби залишався тим самим, а logical_readsкожен раз піднімався на 4.

Тоді я б почав із запуску DBCC і побачив ту саму схему. Я робив це доволі декілька разів, і цифри, про які повідомлялося, відповідали всім тестам.


Більше інформації:

Я також тестую на SQL Server 2012, SP2 - 64 біт (11.0.5343).

Наступні команди DBCC, які ми пробували і не бачили ефекту:

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

Більшість часу DBCC DROPCLEANBUFFERSпрацює, але я час від часу бачу, що він все ще знаходиться у буферному басейні. Незвичайно.

Коли я:

  • DBCC DROPCLEANBUFFERS: Читання збільшується на 24, а логічне читання - на 52.
  • Запустіть SELECT [Col1] FROM dbo.ReadTest;ще раз: показання не збільшуються, але логічно прочитані піднімаються на 6.
  • Додайте пробіл до тексту запиту та повторіть запуск: Читання не збільшується, але логічний_прочитань збільшується на 52 (так само, як і відразу після DBCC DROPCLEANBUFFERS).

Здається, що 52 логічних рахунків рахунків для формування плану та результатів, що означає, що генерація плану спричинила додаткові 46 логічних зчитування. Але фізичне зчитування не повторюється, і все-таки це ті ж 52 логічні читання, що і тоді, коли це потрібно було робити і фізичні читання, отже, logical_readsвони не включають фізичні reads. Я якраз даю зрозуміти, незалежно від того, було це зазначено чи мається на увазі у Питання.

АЛЕ, одна поведінка, яку я помітив, яка відкидає (принаймні трохи), використовуючи існування сторінок даних таблиці в sys.dm_os_buffer_descriptors: вона перезавантажується якимось іншим процесом. Якщо ви ДРОПКЛЯНУВАТИ і перевіряєте негайно, то його вже не буде. Але зачекайте кілька хвилин, і воно з’явиться знову, але цього разу без усіх сторінок даних. У моєму тесті таблиця містить 1 сторінку IAM та 4 сторінки даних. Всі 5 сторінок знаходяться в буферному пулі після того, як я це зробити SELECT. Але коли він перезавантажується якимось іншим процесом, це просто сторінка IAM та 1 сторінка даних. Я подумав, що це може бути SSMS IntelliSense, але я видалив усі посилання на це ім’я об'єкта на вкладці запитів, і він все одно перезавантажується.


як не дивно, я видалив DBCC DROPCLEANBUFFERS(та інші DBCC DROPxxxкоманди) зі своєї тестової установки, оскільки вони не мали ніякої різниці. Якщо встановити базу даних в режимі офлайн, випадають усі буфери та все інше, пов’язане з базою даних.
Макс Вернон

Я розумів те, що ви читаєте як фізичні, і логічне читання починається з буферного пулу, btw.
Макс Вернон

Я також спробував це: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Макс Вернон,

1
@MaxVernon Могла б бути функцією "тримати їх угадати" ;-)
Соломон Руцький

2
@MaxVernon, не забудьте виконати CHECKPOUNTв контексті бази даних до DBCC DROPCLEANBUFFERS.
Дан Гузман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.