Esto puede parecer una pregunta muy básica, y de hecho debería serlo. Sin embargo, como fanático del método científico, me gusta crear una hipótesis, luego probarla para ver si estoy en lo correcto. En este caso, estoy tratando de comprender mejor el resultado de sys.dm_exec_sessions
, y más específicamente, la columna única "lee".
Los Libros en pantalla de SQL Server especifican esto de forma bastante seca como:
Número de lecturas realizadas, por solicitudes en esta sesión, durante esta sesión. No es anulable.
Se podría suponer que esto indicaría el número de páginas leídas del disco para satisfacer las solicitudes emitidas por esta sesión desde el inicio de la sesión. Esta es la hipótesis que pensé probar.
La logical_reads
columna en esa misma tabla se define como:
Número de lecturas lógicas que se han realizado en la sesión. No es anulable.
Por experiencia con SQL Server, creo que esta columna refleja el número de páginas que se han leído tanto desde el disco como en la memoria . En otras palabras, el número total de páginas cada vez leído por la sesión, sin importar donde residen esas páginas. El diferenciador, o propuesta de valor, de tener dos columnas separadas que ofrecen información similar parece ser que uno puede entender la proporción de páginas leídas del disco ( reads
) frente a las leídas del caché del búfer ( logical_reads
) para una sesión específica.
En mi plataforma de prueba, creé una nueva base de datos, creé una sola tabla con un número conocido de páginas de datos, luego leí esa tabla en una nueva sesión. Luego miré sys.dm_exec_sessions
para ver qué decían las columnas reads
y logical_reads
sobre la sesión. En este punto estoy confundido por los resultados. Quizás alguien aquí pueda arrojarme algo de luz sobre esto.
La plataforma de prueba:
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 primera declaración de selección anterior muestra que, de hecho, la tabla consta de 10,000 filas, con 5,025 páginas en total, 5,020 páginas usadas y 5,000 páginas de datos; precisamente como cabría esperar:
La segunda instrucción select confirma que no tenemos nada en memoria para la TestReads
tabla.
En una nueva sesión , hacemos la siguiente consulta, tomando nota del session_id:
USE TestReads;
SET STATISTICS IO ON;
SELECT *
FROM dbo.TestReads;
Como era de esperar, esto lee toda la tabla del disco a la memoria, como se muestra en la salida 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.
En una tercera sesión, inspeccionamos 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 */
Esperaría ver sys.dm_exec_sessions
mostrar al menos 5,000 para ambos reads
y logical_reads
. Por desgracia, veo reads
muestra cero. logical_reads
muestra un número esperado de lecturas en algún lugar al norte de 5,000; muestra 5,020 en mi prueba:
Sé que SQL Server leyó toda la TestReads
tabla en la memoria, en virtud del 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;
¿Qué estoy haciendo mal?
Estoy usando SQL Server 2012 11.0.5343 para esta prueba.
Otros hallazgos:
Si ejecuto lo siguiente:
SELECT des.session_id
, des.reads
, des.logical_reads
FROM sys.dm_exec_sessions des
Veo reads
784 en la sesión donde estoy creando la plataforma de prueba; sin embargo, todas las demás sesiones muestran cero en la reads
columna.
Ahora he actualizado mi instancia de prueba de SQL Server a 11.0.6020; Sin embargo, el resultado es el mismo.
fuente
sys.dm_exec_requests
te dará casi lo mismo que losset statistics io on
resultados.SET STATISTICS IO ON
justo antes de leer de la tabla en la segunda sesión, informa 3 lecturas físicas y 4998 lecturas de lectura anticipada; sin embargosys.dm_exec_sessions
todavía no refleja eso en lareads
columna.STATISTICS IO
i.stack.imgur.com/XbHae.pngreads
campos. Sospecho que funciona de manera muy similar al session_space_usage o cualquier DMV que muestre el uso de tempdb por sesión que no se incrementa hasta que finaliza la "solicitud".Respuestas:
Mi comprensión siempre ha sido que
reads
solo es física (es decir, desde el disco) ylogical_reads
es solo del grupo de búferes (es decir, de la memoria). Hice una prueba rápida con una tabla más pequeña que solo tiene 2 páginas de datos y 3 páginas en total, y lo que veo parece confirmar esas dos definiciones.Una cosa que probablemente le está dando malos resultados es que no está limpiando la memoria. Debe ejecutar lo siguiente entre pruebas para forzarlo a recargarse desde el disco:
Mi configuración de prueba fue solo la siguiente:
Luego ejecuté lo siguiente:
(Sí, estaba probando en la misma sesión en la que estaba ejecutando el DMV, pero eso no sesgó los resultados para el
reads
campo, y si nada más, fue al menos consistente si contribuyó allogical_reads
campo).Para probar, ejecutaría el comando DBCC y luego las dos consultas SELECT. Entonces vería un salto en los campos
reads
ylogical_reads
. Volvería a ejecutar las consultas SELECT y, a veces, vería un salto adicionalreads
.Después de eso, ejecutaba las dos consultas SELECT muchas veces y seguía
reads
siendo la misma, mientras quelogical_reads
aumentaba en 4 cada vez.Entonces comenzaría de nuevo ejecutando el DBCC y vería el mismo patrón. Lo hice varias veces y los números informados fueron consistentes en todas las ejecuciones de prueba.
Más información:
También estoy probando en SQL Server 2012, SP2 - 64 bits (11.0.5343).
Los siguientes comandos DBCC hemos intentado y no hemos visto ningún efecto:
La mayoría de las veces
DBCC DROPCLEANBUFFERS
funciona, pero ocasionalmente veo que todavía está en el Buffer Pool. Impar.Cuando yo:
DBCC DROPCLEANBUFFERS
: Las lecturas aumentan en 24 y logical_reads aumentan en 52.SELECT [Col1] FROM dbo.ReadTest;
nuevo: las lecturas no aumentan, pero los hilos lógicos aumentan en 6.DBCC DROPCLEANBUFFERS
).Parece que las 52 lecturas lógicas explican la generación del plan y los resultados, lo que implica que la generación del plan causó las 46 lecturas lógicas adicionales. Pero las lecturas físicas no vuelven a subir y, sin embargo, son las mismas 52 lecturas lógicas que cuando necesitaba hacer también las lecturas físicas, por
logical_reads
lo tanto , no incluye las físicasreads
. Solo estoy aclarando este punto, ya sea que se haya declarado o implícito en la Pregunta.PERO, un comportamiento que noté que se deshace (al menos un poco) usando la existencia de las páginas de datos de la tabla en
sys.dm_os_buffer_descriptors
: otro proceso lo vuelve a cargar. Si DROPCLEANBUFFERS y comprueba de inmediato, entonces debería desaparecer. Pero espere unos minutos y aparecerá de nuevo, pero esta vez sin todas las páginas de datos. En mi prueba, la tabla tiene 1 página IAM y 4 páginas de datos. Las 5 páginas están en el grupo de búferes después de hacer elSELECT
. Pero cuando algún otro proceso lo vuelve a cargar, es solo la página IAM y 1 página de datos. Pensé que podría ser SSMS IntelliSense, pero eliminé todas las referencias a ese nombre de objeto en mi pestaña de consulta y todavía se vuelve a cargar.fuente
DBCC DROPCLEANBUFFERS
(y otrosDBCC DROPxxx
comandos) de mi plataforma de prueba porque no hicieron ninguna diferencia. Establecer la base de datos fuera de línea elimina todos los búferes y todo lo demás asociado con la base de datos.DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
CHECKPOUNT
en el contexto de la base de datos antesDBCC DROPCLEANBUFFERS
.