Các cột đọc các dòng trên Nhật Bản trong sys.dm_exec_sments thực sự chỉ ra điều gì?


10

Đây có vẻ như là một câu hỏi rất cơ bản, và thực sự nó nên như vậy. Tuy nhiên, là một fan hâm mộ của phương pháp khoa học, tôi thích tạo ra một giả thuyết, sau đó kiểm tra nó để xem tôi có đúng không. Trong trường hợp này, tôi đang cố gắng hiểu rõ hơn về đầu ra sys.dm_exec_sessionsvà cụ thể hơn là cột "đọc".

Sách SQL Server trực tuyến khá khô khan chỉ định điều này là:

Số lần đọc được thực hiện, theo yêu cầu trong phiên này, trong phiên này. Không phải là nullable.

Người ta có thể cho rằng điều này sẽ chỉ ra số lượng trang được đọc từ đĩa để đáp ứng các yêu cầu được đưa ra bởi phiên này kể từ khi bắt đầu phiên. Đây là giả thuyết tôi nghĩ tôi muốn thử nghiệm.

Các logical_readscột ở chỗ cùng một bảng được định nghĩa là:

Số lần đọc logic đã được thực hiện trên phiên. Không phải là nullable.

Từ kinh nghiệm sử dụng SQL Server, tôi tin rằng cột này phản ánh số lượng trang đã được đọc cả từ đĩatrong bộ nhớ . Nói cách khác, tổng số trang từng được đọc bởi phiên, bất kể các trang đó nằm ở đâu. Sự khác biệt, hoặc đề xuất giá trị, có hai cột riêng biệt cung cấp thông tin tương tự nhau dường như có thể hiểu tỷ lệ các trang đọc từ đĩa ( reads) so với các trang được đọc từ bộ đệm bộ đệm ( logical_reads) cho một phiên cụ thể.

Trên thiết bị thử nghiệm của tôi, tôi đã tạo một cơ sở dữ liệu mới, tạo một bảng duy nhất với số lượng trang dữ liệu đã biết, sau đó đọc bảng đó trong một phiên mới. Sau đó, tôi nhìn vào sys.dm_exec_sessionsđể xem những gì readslogical_readscột nói về phiên. Tại thời điểm này tôi bị bối rối bởi kết quả. Có lẽ ai đó ở đây có thể làm sáng tỏ điều này cho tôi.

Giàn khoan thử nghiệm:

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;

Câu lệnh chọn đầu tiên ở trên cho thấy trên thực tế, bảng này bao gồm 10.000 hàng, với tổng số 5.025 trang, 5.020 trang được sử dụng và 5.000 trang dữ liệu; chính xác như người ta mong đợi:

nhập mô tả hình ảnh ở đây

Câu lệnh chọn thứ hai xác nhận chúng ta không có gì trong bộ nhớ cho TestReadsbảng.

Trong phiên mới , chúng tôi thực hiện truy vấn sau, lưu ý đến session_id:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

Như mọi người mong đợi, cái này đọc toàn bộ bảng từ đĩa vào bộ nhớ, như thể hiện trong đầu ra từ 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.

Trong phiên thứ ba , chúng tôi kiểm tra 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 */

Tôi hy vọng sẽ thấy sys.dm_exec_sessionschương trình ít nhất 5.000 cho cả hai readslogical_reads. Than ôi, tôi thấy readscho thấy không. logical_readskhông hiển thị số lần đọc dự kiến ​​ở đâu đó ở phía bắc 5.000 - nó hiển thị 5.020 trong thử nghiệm của tôi:

nhập mô tả hình ảnh ở đây

Tôi biết SQL Server đọc toàn bộ TestReadsbảng vào bộ nhớ, nhờ vào 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;

nhập mô tả hình ảnh ở đây

Tôi đang làm gì sai?

Tôi đang sử dụng SQL Server 2012 11.0.5343 cho bài kiểm tra này.


Phát hiện thêm:

Nếu tôi chạy như sau:

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

Tôi thấy reads784 trong phiên tôi đang tạo giàn thử; tuy nhiên tất cả các phiên khác hiển thị số không trong readscột.

Bây giờ tôi đã cập nhật phiên bản thử nghiệm SQL Server của mình lên 11.0.6020; tuy nhiên kết quả là như nhau.


sys.dm_exec_requestssẽ cho bạn gần giống như set statistics io onkết quả.
Kin Shah

1
Thú vị SET STATISTICS IO ONngay trước khi tôi đọc từ bảng trong phiên thứ 2 báo cáo 3 lần đọc vật lý và 4998 lần đọc trước; tuy nhiên sys.dm_exec_sessionsvẫn không phản ánh điều đó trong readscột.
Max Vernon

2
Vào năm 2012, tôi thường xuyên thấy 0 cho cả đọc và đọc logic mặc dù kết quả khác không được báo cáo từ STATISTICS IO i.stack.imgur.com/XbHae.png
Martin Smith

1
Trên thực tế, tôi thấy cả hai cột bằng 0 với cách tiếp cận của tôi trên tất cả các phiên bản tôi đã thử nghiệm từ 2008 đến SQL2016CTP3
Martin Smith

1
@MartinSmith và Max: Tôi cũng đã thấy sự chậm trễ trong một số gia số của các readslĩnh vực. Tôi nghi ngờ nó hoạt động giống như session_space_usage hoặc bất kỳ DMV nào hiển thị mức sử dụng tempdb cho mỗi phiên không tăng cho đến khi "yêu cầu" kết thúc.
Solomon Rutzky

Câu trả lời:


2

Sự hiểu biết của tôi luôn luôn readschỉ là vật lý (tức là từ đĩa) và logical_readschỉ từ Bộ đệm (tức là từ bộ nhớ). Tôi đã thực hiện một thử nghiệm nhanh với một bảng nhỏ hơn chỉ có 2 trang dữ liệu và tổng cộng 3 trang và những gì tôi đang thấy dường như xác nhận hai định nghĩa đó.

Một điều có thể mang lại cho bạn kết quả tồi tệ là bạn không xóa bộ nhớ. Bạn nên chạy các bước sau giữa các lần kiểm tra để buộc nó tải lại từ đĩa:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

Thiết lập thử nghiệm của tôi chỉ là như sau:

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

Sau đó tôi chạy như sau:

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

(Có, tôi đã thử nghiệm trong cùng một phiên mà tôi đang chạy DMV, nhưng điều đó không làm sai lệch kết quả cho readslĩnh vực này, và nếu không có gì khác, ít nhất là nhất quán nếu nó đóng góp cho logical_readslĩnh vực này.)

Để kiểm tra tôi sẽ chạy lệnh DBCC và sau đó là hai truy vấn CHỌN. Sau đó, tôi sẽ thấy một bước nhảy trong cả lĩnh vực readslogical_reads. Tôi sẽ chạy lại các truy vấn CHỌN và đôi khi tôi sẽ thấy một bước nhảy bổ sung reads.

Sau đó, tôi sẽ chạy hai truy vấn CHỌN nhiều lần và readssẽ vẫn giữ nguyên trong khi truy vấn logical_readstăng 4 lần mỗi lần.

Sau đó tôi sẽ bắt đầu lại với việc chạy DBCC và thấy mô hình tương tự. Tôi đã làm điều này khá nhiều lần và các con số được báo cáo là nhất quán trong tất cả các lần chạy thử.


Thêm thông tin:

Tôi cũng đang thử nghiệm trên SQL Server 2012, SP2 - 64 bit (11.0.5343).

Các lệnh DBCC sau đây chúng tôi đã thử và thấy không có hiệu lực:

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

Hầu hết thời gian DBCC DROPCLEANBUFFERSkhông hoạt động, nhưng tôi thỉnh thoảng thấy rằng nó vẫn ở trong Vùng đệm. Lạ

Khi tôi:

  • DBCC DROPCLEANBUFFERS: Số lần đọc tăng lên 24 và logic_reads tăng 52.
  • Chạy SELECT [Col1] FROM dbo.ReadTest;lại: Các lần đọc không tăng lên, nhưng logic_reads tăng lên 6.
  • Thêm một khoảng trắng vào văn bản truy vấn và chạy lại: Số lần đọc không tăng lên, nhưng logic_reads tăng lên 52 (giống như ngay sau khi DBCC DROPCLEANBUFFERS).

Dường như 52 tài khoản đọc logic để tạo kế hoạch và kết quả, ngụ ý rằng việc tạo kế hoạch đã gây ra 46 lần đọc logic bổ sung. Nhưng các lần đọc vật lý không tăng trở lại và đó cũng là 52 lần đọc logic giống như khi nó cũng cần thực hiện các lần đọc vật lý, do đó logical_readskhông bao gồm vật lý reads. Tôi chỉ làm rõ quan điểm này, cho dù nó có được nêu hay ngụ ý trong Câu hỏi hay không.

NHƯNG, một hành vi tôi đã nhận thấy rằng sẽ loại bỏ (ít nhất một chút) bằng cách sử dụng sự tồn tại của các trang dữ liệu của bảng sys.dm_os_buffer_descriptors: nó được tải lại bởi một số quy trình khác. Nếu bạn DROPCLEANBUFFERS và kiểm tra ngay lập tức, thì nó sẽ biến mất. Nhưng đợi vài phút và nó lại xuất hiện, nhưng lần này không có tất cả các trang dữ liệu. Trong thử nghiệm của tôi, bảng có 1 trang IAM và 4 trang dữ liệu. Tất cả 5 trang nằm trong vùng đệm sau khi tôi thực hiện SELECT. Nhưng khi nó được tải lại bởi một số quy trình khác, nó chỉ là trang IAM và 1 trang dữ liệu. Tôi nghĩ đó có thể là SSMS IntelliSense, nhưng tôi đã xóa tất cả các tham chiếu đến tên đối tượng đó trong tab truy vấn của mình và nó vẫn được tải lại.


thật thú vị, tôi đã gỡ bỏ DBCC DROPCLEANBUFFERS(và các DBCC DROPxxxlệnh khác ) khỏi thiết bị thử nghiệm của mình vì chúng không tạo ra sự khác biệt nào. Đặt cơ sở dữ liệu ngoại tuyến sẽ loại bỏ tất cả các bộ đệm và mọi thứ khác được liên kết với cơ sở dữ liệu.
Max Vernon

Tôi đã hiểu theo cách tương tự như bạn về việc đọc là vật lý và việc đọc logic bắt đầu từ nhóm bộ đệm, btw.
Max Vernon

Tôi cũng đã thử nó với: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Max Vernon

1
@MaxVernon Có thể là tính năng "giữ 'em đoán" ;-)
Solomon Rutzky

2
@MaxVernon, đừng quên thực hiện CHECKPOUNTtrong bối cảnh cơ sở dữ liệu trước đó DBCC DROPCLEANBUFFERS.
Dan Guzman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.