Isso pode parecer uma pergunta muito básica, e de fato deveria ser. No entanto, como fã do método científico, gosto de criar uma hipótese e testá-la para ver se estou correta. Nesse caso, estou tentando entender melhor a saída sys.dm_exec_sessions
e, mais especificamente, a única coluna "lê".
Os Manuais Online do SQL Server especificam isso secamente como:
Número de leituras realizadas, por solicitações nesta sessão, durante esta sessão. Não é anulável.
Pode-se presumir que isso indica o número de páginas lidas do disco para satisfazer as solicitações emitidas por esta sessão desde o início da sessão. Essa é a hipótese que pensei em testar.
A logical_reads
coluna nessa mesma tabela é definida como:
Número de leituras lógicas que foram executadas na sessão. Não é anulável.
Por experiência usando o SQL Server, acredito que esta coluna reflete o número de páginas que foram lidas no disco e na memória . Em outras palavras, o número total de páginas já lidas pela sessão, não importa onde essas páginas residam. O diferencial, ou proposição de valor, de ter duas colunas separadas que oferecem informações semelhantes parece ser a capacidade de entender a proporção de páginas lidas no disco ( reads
) versus as lidas no cache do buffer ( logical_reads
) para uma sessão específica.
No meu equipamento de teste, criei um novo banco de dados, criei uma única tabela com um número conhecido de páginas de dados e depois li essa tabela em uma nova sessão. Então olhei sys.dm_exec_sessions
para ver o que as colunas reads
e logical_reads
disseram sobre a sessão. Neste ponto, estou confuso com os resultados. Talvez alguém aqui possa esclarecer isso para mim.
O equipamento de teste:
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;
A primeira instrução de seleção acima mostra que, de fato, a tabela consiste em 10.000 linhas, com 5.025 páginas no total, 5.020 páginas usadas e 5.000 páginas de dados; exatamente como seria de esperar:
A segunda instrução select confirma que não temos nada na memória para a TestReads
tabela.
Em uma nova sessão , fazemos a seguinte consulta, anotando o session_id:
USE TestReads;
SET STATISTICS IO ON;
SELECT *
FROM dbo.TestReads;
Como seria de esperar, isso lê a tabela inteira do disco para a memória, conforme mostrado na saída 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.
Em uma terceira sessão, inspecionamos 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 */
Eu esperaria ver sys.dm_exec_sessions
mostrar pelo menos 5.000 para ambos reads
e logical_reads
. Infelizmente, eu vejo reads
mostra zero. logical_reads
mostra um número esperado de leituras em algum lugar ao norte de 5.000 - mostra 5.020 no meu teste:
Eu sei que o SQL Server leu a TestReads
tabela inteira na memória, em virtude da 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;
O que estou fazendo de errado?
Estou usando o SQL Server 2012 11.0.5343 para este teste.
Outras descobertas:
Se eu executar o seguinte:
SELECT des.session_id
, des.reads
, des.logical_reads
FROM sys.dm_exec_sessions des
Eu vejo o reads
784 na sessão em que estou criando o equipamento de teste; no entanto, todas as outras sessões mostram zero na reads
coluna.
Atualizei agora minha instância de teste do SQL Server para 11.0.6020; no entanto, o resultado é o mesmo.
fonte
sys.dm_exec_requests
lhe dará quase o mesmo que osset statistics io on
resultados.SET STATISTICS IO ON
antes de ler a tabela da 2ª sessão relata 3 leituras físicas e 4998 leituras de leitura antecipada; no entanto,sys.dm_exec_sessions
ainda não reflete isso nareads
coluna.STATISTICS IO
i.stack.imgur.com/XbHae.pngreads
campos. Eu suspeito que ele funciona muito como o session_space_usage ou qualquer DMV que mostre o uso do tempdb por sessão que não é incrementado até que a "solicitação" seja concluída.Respostas:
Meu entendimento sempre foi que
reads
é apenas físico (ou seja, do disco) elogical_reads
é apenas do Buffer Pool (ou seja, da memória). Fiz um teste rápido com uma tabela menor, com apenas 2 páginas de dados e 3 páginas no total, e o que estou vendo parece confirmar essas duas definições.Uma coisa que provavelmente está lhe dando maus resultados é que você não está limpando a memória. Você deve executar o seguinte entre testes para forçar o recarregamento do disco:
Minha configuração de teste foi a seguinte:
Em seguida, executei o seguinte:
(Sim, eu estava testando na mesma sessão em que estava executando o DMV, mas isso não distorceu os resultados para o
reads
campo e, se nada mais, era pelo menos consistente se contribuísse para ological_reads
campo.)Para testar, eu executaria o comando DBCC e as duas consultas SELECT. Então eu veria um salto nos campos
reads
elogical_reads
. Eu executava as consultas SELECT novamente e, às vezes, via um salto adicionalreads
.Depois disso, eu executava as duas consultas SELECT várias vezes e as
reads
permaneceriam as mesmas, enquanto aslogical_reads
quatro aumentavam todas as vezes.Eu começaria novamente executando o DBCC e veria o mesmo padrão. Fiz isso várias vezes e os números relatados foram consistentes em todas as execuções de teste.
Mais informações:
Também estou testando no SQL Server 2012, SP2 - 64 bits (11.0.5343).
Os seguintes comandos do DBCC tentamos e não vimos nenhum efeito:
Na maioria das vezes
DBCC DROPCLEANBUFFERS
funciona, mas ocasionalmente vejo que ele ainda está no Buffer Pool. Ímpar.Quando eu:
DBCC DROPCLEANBUFFERS
: As leituras aumentam 24 e as lições_lógicas aumentam 52.SELECT [Col1] FROM dbo.ReadTest;
novamente: as leituras não aumentam, mas as lições_lógicas aumentam 6.DBCC DROPCLEANBUFFERS
).Parece que as 52 leituras lógicas são responsáveis pela geração do plano e pelos resultados, o que implica que a geração do plano causou 46 leituras lógicas adicionais. Mas as leituras físicas não sobem novamente e, no entanto, são as mesmas 52 leituras lógicas que eram quando precisavam também fazer as leituras físicas, portanto
logical_reads
, não incluem as físicasreads
. Estou apenas esclarecendo esse ponto, esteja ele sendo ou não declarado ou implícito na questão.MAS, um comportamento que notei que se destaca (pelo menos um pouco) usando a existência das páginas de dados da tabela
sys.dm_os_buffer_descriptors
: ela é recarregada por algum outro processo. Se você DROPCLEANBUFFERS e verificar imediatamente, ele deve ter desaparecido. Mas espere alguns minutos e ele aparecerá novamente, mas desta vez sem todas as páginas de dados. No meu teste, a tabela possui 1 página IAM e 4 páginas de dados. Todas as 5 páginas estão no buffer pool depois que eu faço oSELECT
. Mas quando é recarregado por algum outro processo, é apenas a página do IAM e a 1 página de dados. Eu pensei que poderia ser o SSMS IntelliSense, mas removi todas as referências a esse nome de objeto na minha guia de consulta e ele ainda é recarregado.fonte
DBCC DROPCLEANBUFFERS
(e outrosDBCC DROPxxx
comandos) do meu equipamento de teste porque eles não fizeram nenhuma diferença. Definir o banco de dados offline descarta todos os buffers e tudo o mais associado ao banco de dados.DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
CHECKPOUNT
no contexto do banco de dados antesDBCC DROPCLEANBUFFERS
.