Sys.dm_exec_sessions içindeki "okur" sütunu gerçekte neyi gösterir?


10

Bu çok temel bir soru gibi görünebilir ve gerçekten de olması gerekir. Ancak, bilimsel yöntemin bir hayranı olarak, bir hipotez oluşturmayı, sonra doğru olup olmadığımı görmek için test etmeyi seviyorum. Bu durumda, daha iyi sys.dm_exec_sessionstek sütun "okur" çıktısını daha iyi anlamaya çalışıyorum .

SQL Server Books Online'da bunu oldukça kuru olarak belirtilir:

Bu oturumdaki isteklere göre, bu oturum sırasında gerçekleştirilen okuma sayısı. Null değeri yoktur.

Bunun , oturumun başlamasından bu yana bu oturum tarafından yayınlanan istekleri karşılamak için diskten okunan sayfa sayısını göstereceği varsayılabilir . Test edeceğimi düşündüğüm hipotez bu.

logical_readsAynı tabloda, kolon gibi tanımlanır:

Oturumda gerçekleştirilen mantıksal okuma sayısı. Null değeri yoktur.

SQL Server kullanma deneyiminden, bu sütunun hem diskten hem de bellekten okunan sayfa sayısını yansıttığına inanıyorum . Başka bir deyişle, toplam sayfa sayısı hiç oturumda, bu sayfalar ikamet olursa olsun tarafından okunan. Benzer bilgiler sunan iki ayrı sütuna sahip olmanın farklılaştırıcısı veya değer önerisi, belirli bir oturum için diskten ( ) okunan sayfaların arabellek önbelleğinden ( ) okunan sayfalara oranını anlayabilecek gibi görünmektedir .readslogical_reads

Test cihazımda yeni bir veritabanı oluşturdum, bilinen sayıda sayfa içeren tek bir tablo oluşturdum ve sonra bu tabloyu yeni bir oturumda okudum. Sonra oturumu hakkında ve sütunlar sys.dm_exec_sessionsne dedi görmek için baktım . Bu noktada sonuçlardan şaşkınım. Belki buradaki biri benim için buna biraz ışık tutabilir.readslogical_reads

Test düzeneği:

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;

Yukarıdaki ilk select deyimi aslında tablonun toplamda 5.025 sayfa, 5.020 kullanılmış sayfa ve 5.000 veri sayfası içeren 10.000 satırdan oluştuğunu; tam olarak beklendiği gibi:

resim açıklamasını buraya girin

İkinci select deyimi TestReadstablo için bellekte hiçbir şey olmadığını doğrular .

Bir de yeni oturum , biz session_id dikkat ederek, aşağıdaki sorguyu yapın:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

Beklendiği gibi, bu tüm tabloyu çıktıdaki gibi diskten belleğe okur 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.

Bir de üçüncü oturumda, biz incelemek 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 */

Ben ve her ikisi için en az 5.000 sys.dm_exec_sessionsgösteri görmek beklenir . Ne yazık ki, sıfır gösterir. 5.000'in kuzeyinde bir yerde beklenen sayıda okuma gösteriyor - testimde 5.020 gösteriyor:readslogical_readsreadslogical_reads

resim açıklamasını buraya girin

SQL Server DMV TestReadssayesinde tüm tabloyu belleğe okuduğunu biliyorum sys_dm_os_buffer_descriptors:

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;

resim açıklamasını buraya girin

Neyi yanlış yapıyorum?

Bu test için SQL Server 2012 11.0.5343 kullanıyorum.


Diğer bulgular:

Aşağıdakileri çalıştırırsam:

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

readsTest donanımını oluşturduğum oturumda 784'ü görüyorum ; ancak diğer tüm oturumlar readssütunda sıfır gösterir .

Şimdi SQL Server test örneğimi 11.0.6020 olarak güncelledim; ancak sonuç aynıdır.


sys.dm_exec_requestsset statistics io onsonuçlarla hemen hemen aynı olacaktır .
Kin Shah

1
SET STATISTICS IO ON2. oturumdaki tablodan okumadan hemen önce ilginç olan 3 fiziksel okuma ve 4998 önceden okuma raporları; ancak sys.dm_exec_sessionsyine de readssütunda bunu yansıtmaz .
Max Vernon


1
Aslında, 2008'den SQL2016CTP3'e test ettiğim tüm sürümlerde yaklaşımımla her iki sütunu da sıfır görüyorum
Martin Smith

1
@MartinSmith ve Max: readsAlanların bazı artışlarında da bir gecikme görüyorum . Ben çok session_space_usage veya "istek" bitene kadar artmaz oturum başına tempdb kullanımını gösteren herhangi bir DMV gibi çalışır şüpheli.
Solomon Rutzky

Yanıtlar:


2

Benim anlayışım her zaman readssadece fiziksel (yani diskten) ve logical_readssadece Tampon Havuzundan (yani bellekten) olmuştur. Sadece 2 veri sayfası ve toplam 3 sayfa olan daha küçük bir tabloyla hızlı bir test yaptım ve gördüğüm şey bu iki tanımı onaylıyor gibi görünüyor.

Muhtemelen size kötü sonuçlar veren bir şey, hafızayı temizlememenizdir. Diskten yeniden yüklemeye zorlamak için sınamalar arasında aşağıdakileri çalıştırmalısınız:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

Test kurulumum sadece şuydu:

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

Sonra aşağıdakileri koştum:

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

(Evet, DMV'yi yürüttüğüm aynı oturumda test yapıyordum, ancak bu readsalanın sonuçlarını çarpıtmadı ve başka bir şey yoksa, logical_readsalana katkıda bulunmuşsa en azından tutarlıydı .)

Test için DBCC komutunu ve ardından iki SELECT sorgusunu çalıştırırım. Sonra hem readsve logical_readsalanlarında bir sıçrama görecektim . SELECT sorgularını tekrar çalıştırırdım ve bazen ek bir sıçrama görürdüm reads.

Bundan sonra, iki SELECT sorgusunu birçok kez çalıştırırdım ve her seferinde 4 arttıkça readsaynı kalacaktı logical_reads.

Sonra DBCC çalışan ile başlamak ve aynı desen görmek. Bunu birkaç kez yaptım ve bildirilen sayılar tüm test çalışmalarında tutarlıydı.


Daha fazla bilgi:

Ayrıca SQL Server 2012, SP2 - 64 bit (11.0.5343) üzerinde de test yapıyorum.

Aşağıdaki DBCC komutlarını hem denedik hem de hiçbir etki görmedik:

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

Çoğu zaman DBCC DROPCLEANBUFFERSişe yarar, ancak bazen hala Tampon Havuzunda olduğunu görüyorum. Garip.

Ne zaman ben:

  • DBCC DROPCLEANBUFFERS: Okumalar 24, mantıksal_dizeler 52 artar.
  • SELECT [Col1] FROM dbo.ReadTest;Tekrar çalıştırın : Okumalar artmaz, ancak logical_reads 6 artar.
  • Sorgu metnine bir boşluk ekleyin ve yeniden çalıştırın: Okumalar yukarı gitmez, ancak mantıksal_dekler 52 kadar artar (tıpkı hemen ardından olduğu gibi DBCC DROPCLEANBUFFERS).

52 mantıksal okumanın plan oluşturma ve sonuçları açıkladığı görülüyor, bu da plan oluşturma işleminin ek 46 mantıksal okumaya neden olduğunu ima ediyor. Ancak fiziksel okumalar tekrar yükselmez ve yine de fiziksel okumaları yapması gerektiğinde olduğu gibi 52 mantıksal okuma ile aynıdır, bu nedenle logical_readsfiziksel içermez reads. Soruda belirtilmiş ya da ima edilmiş olsun ya da olmasın bu noktayı açıklığa kavuşturuyorum.

ANCAK, fark ettim bir davranış (en azından biraz) tablonun veri sayfalarının varlığını kullanarak atar sys.dm_os_buffer_descriptors: başka bir işlem tarafından yeniden yüklenir. DROPCLEANBUFFERS yapar ve hemen kontrol ederseniz, o zaman gitmelidir. Ancak birkaç dakika bekleyin ve tekrar görünür, ancak bu sefer tüm veri sayfaları olmadan. Testimde tabloda 1 IAM sayfası ve 4 veri sayfası var. Ben yaptıktan sonra tüm 5 sayfa arabellek havuzunda SELECT. Ancak başka bir işlemle yeniden yüklendiğinde, sadece IAM sayfası ve 1 veri sayfasıdır. SSMS IntelliSense olabileceğini düşündüm, ancak sorgu sekmesinde bu nesne adına yapılan tüm referansları kaldırdım ve yine de yeniden yükleniyor.


ne kadar tuhaf ki, DBCC DROPCLEANBUFFERS(ve diğer DBCC DROPxxxkomutları) test donanımımdan kaldırdım çünkü herhangi bir fark yaratmadılar. Veritabanını çevrimdışı olarak ayarlamak tüm arabellekleri ve veritabanıyla ilişkili diğer her şeyi bırakır.
Max Vernon

Okumaların fiziksel olduğu konusunda aynı anlayıştaydım ve mantıksal okumalar tampon havuzundan başlar, btw.
Max Vernon

Ayrıca şunu da denedim: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Max Vernon

1
@MaxVernon "Onları tahmin et" özelliği olabilir ;-)
Solomon Rutzky

2
@ MaxVernon, CHECKPOUNTönce veritabanı bağlamında bir çalıştırmayı unutmayın DBCC DROPCLEANBUFFERS.
Dan Guzman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.