Что на самом деле указывает столбец «чтения» в sys.dm_exec_sessions?

10

Это может показаться очень простым вопросом, и так оно и должно быть. Однако, как поклонник научного метода, мне нравится создавать гипотезу, а затем проверять ее, чтобы убедиться, что я прав. В этом случае я пытаюсь лучше понять вывод sys.dm_exec_sessions, а точнее, один столбец «читает».

Электронная документация по SQL Server довольно сухо определяет это как:

Количество операций чтения, выполненных по запросам в этом сеансе, в течение этого сеанса. Не обнуляется.

Можно предположить, что это будет указывать количество страниц, прочитанных с диска, чтобы удовлетворить запросы, выданные этим сеансом с начала сеанса. Это гипотеза, которую я думал проверить.

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;

Первый оператор select выше показывает, что на самом деле таблица состоит из 10 000 строк, с общим количеством страниц 5 025, 5 020 используемых страниц и 5 000 страниц данных; именно так, как и следовало ожидать:

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

Второй оператор select подтверждает, что у нас ничего нет в памяти для TestReadsтаблицы.

В новом сеансе мы делаем следующий запрос, принимая к сведению идентификатор_сессии:

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перед тем как я прочитал из таблицы во втором сеансе отчеты о 3 физических чтениях и 4998 чтениях с опережением чтения; Однако sys.dm_exec_sessionsвсе еще не отражает это в readsстолбце.
Макс Вернон
2
В 2012 году я часто вижу 0 как для чтения, так и для логического чтения, несмотря на ненулевые результаты, полученные на STATISTICS IO i.stack.imgur.com/XbHae.png
Martin Smith
1
На самом деле я вижу оба столбца нулевыми с моим подходом во всех выпусках, которые я тестировал с 2008 по SQL2016CTP3
Мартин Смит
1
@MartinSmith и Макс: я также видел задержку в некоторых приращениях 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: она перезагружается каким-то другим процессом. Если вы DROPCLEANBUFFERS и проверьте сразу, то это должно быть пропало. Но подождите несколько минут, и он появится снова, но на этот раз без всех страниц данных. В моем тесте таблица имеет 1 страницу IAM и 4 страницы данных. Все 5 страниц находятся в пуле буферов после того, как я сделаю SELECT. Но когда он перезагружается каким-то другим процессом, это просто страница IAM и 1 страница данных. Я думал, что это может быть SSMS IntelliSense, но я удалил все ссылки на это имя объекта на моей вкладке запроса, и он все еще перезагружается.

Соломон Руцкий
источник
как ни странно, я удалил DBCC DROPCLEANBUFFERS(и другие DBCC DROPxxxкоманды) из моей тестовой установки, потому что они не имели никакого значения. При отключении базы данных все буферы удаляются, а все остальное связывается с базой данных.
Макс Вернон
У меня было такое же понимание, как и у вас, что чтение является физическим, и логические чтения начинаются с буферного пула, кстати.
Макс Вернон
Я также попробовал это с: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Максом Верноном
1
@MaxVernon Может быть, функция «держи их в догадках» ;-)
Соломон Руцки
2
@MaxVernon, не забудьте выполнить CHECKPOUNTв контексте базы данных до DBCC DROPCLEANBUFFERS.
Дан Гусман