Tại sao CHECKDB đọc tệp nhật ký giao dịch trên cơ sở dữ liệu với bảng được tối ưu hóa bộ nhớ?


16

tl; dr : tại sao CHECKDB đọc nhật ký giao dịch cho cơ sở dữ liệu người dùng với các bảng được tối ưu hóa bộ nhớ?


Dường như CHECKDB đang đọc tệp nhật ký giao dịch của cơ sở dữ liệu người dùng khi kiểm tra trên một trong các cơ sở dữ liệu của tôi - đặc biệt là cơ sở dữ liệu sử dụng các bảng OLTP trong bộ nhớ.

CHECKDB cho cơ sở dữ liệu này vẫn hoàn thành trong một khoảng thời gian hợp lý, vì vậy tôi hầu như chỉ tò mò về hành vi; nhưng đây chắc chắn là khoảng thời gian dài nhất cho CHECKDB trong tất cả các cơ sở dữ liệu trong trường hợp này.

Khi xem qua sử thi " CHECKDB từ mọi góc độ " của Paul Randal : Mô tả đầy đủ về tất cả các giai đoạn CHECKDB, "Tôi thấy rằng CHECKDB trước SQL 2005 đã sử dụng để đọc nhật ký để có được cái nhìn nhất quán về cơ sở dữ liệu. Nhưng vì đây là năm 2016, nó sử dụng ảnh chụp nhanh cơ sở dữ liệu nội bộ.

Tuy nhiên, một trong những điều kiện tiên quyết để chụp ảnh nhanh là:

Cơ sở dữ liệu nguồn không được chứa tập tin MEMORY_OPTIMIZED_DATA

Cơ sở dữ liệu người dùng của tôi có một trong những nhóm fileg này, vì vậy có vẻ như ảnh chụp nhanh ở ngoài bàn.

Theo các tài liệu CHECKDB :

Nếu không thể tạo ảnh chụp nhanh hoặc TABLOCK được chỉ định, DBCC CHECKDB có được các khóa để có được tính nhất quán cần thiết. Trong trường hợp này, khóa cơ sở dữ liệu độc quyền được yêu cầu để thực hiện kiểm tra phân bổ và khóa bảng chia sẻ được yêu cầu để thực hiện kiểm tra bảng.

Được rồi, vì vậy chúng tôi đang thực hiện khóa cơ sở dữ liệu và bảng thay vì ảnh chụp nhanh. Nhưng điều đó vẫn không giải thích được tại sao nó phải đọc nhật ký giao dịch. Vì vậy, những gì cho?

Tôi đã cung cấp một kịch bản dưới đây để tái tạo kịch bản. Nó sử dụng sys.dm_io_virtual_file_statsđể xác định các tệp nhật ký đọc.

Lưu ý rằng hầu hết thời gian nó đọc một phần nhỏ của nhật ký (480 KB), nhưng đôi khi nó đọc nhiều hơn (48,2 MB). Trong kịch bản sản xuất của tôi, nó đọc hầu hết tệp nhật ký (~ 1,3 GB của tệp 2 GB) mỗi đêm vào lúc nửa đêm khi chúng tôi chạy CHECKDB.

Đây là một ví dụ về các kết quả đầu ra mà tôi đã nhận được cho đến nay với tập lệnh:

collection_time            num_of_reads     num_of_bytes_read
2018-04-04 15:12:29.203    106              50545664

Hoặc này:

collection_time            num_of_reads     num_of_bytes_read
2018-04-04 15:25:14.227    1                491520

Nếu tôi thay thế các đối tượng được tối ưu hóa bộ nhớ bằng các bảng thông thường, đầu ra trông như thế này:

collection_time            num_of_reads     num_of_bytes_read
2018-04-04 15:21:03.207    0                0

Tại sao CHECKDB đọc tệp nhật ký? Và đặc biệt, tại sao đôi khi nó đọc một phần lớn hơn của tệp nhật ký?

Đây là kịch bản thực tế:

-- let's have a fresh DB
USE [master];

IF (DB_ID(N'LogFileRead_Test') IS NOT NULL) 
BEGIN
    ALTER DATABASE [LogFileRead_Test]
    SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [LogFileRead_Test];
END

GO
CREATE DATABASE [LogFileRead_Test]

GO
ALTER DATABASE [LogFileRead_Test]
MODIFY FILE
(
    NAME = LogFileRead_Test_log,
    SIZE = 128MB
);

-- Hekaton-yeah, I want memory optimized data
GO
ALTER DATABASE [LogFileRead_Test]
ADD FILEGROUP [LatencyTestInMemoryFileGroup] CONTAINS MEMORY_OPTIMIZED_DATA;

GO
ALTER DATABASE [LogFileRead_Test]
ADD FILE 
(
    NAME = [LatencyTestInMemoryFile], 
    FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL13.SQL2016\MSSQL\DATA\LogFileRead_Test_SessionStateInMemoryFile'
) TO FILEGROUP [LatencyTestInMemoryFileGroup];

GO
USE [LogFileRead_Test]

GO
CREATE TYPE [dbo].[InMemoryIdTable] AS TABLE (
    [InMemoryId] NVARCHAR (88) COLLATE Latin1_General_100_BIN2 NOT NULL,
    PRIMARY KEY NONCLUSTERED HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240))
    WITH (MEMORY_OPTIMIZED = ON);

GO
CREATE TABLE [dbo].[InMemoryStuff] (
    [InMemoryId]   NVARCHAR (88)    COLLATE Latin1_General_100_BIN2 NOT NULL,
    [Created]     DATETIME2 (7)    NOT NULL,
    CONSTRAINT [PK_InMemoryStuff_InMemoryId] PRIMARY KEY NONCLUSTERED HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240)
)
WITH (MEMORY_OPTIMIZED = ON);

GO
-- RBAR is the new black (we need some logs to read)
declare @j int = 0;
while @j < 100000
begin
    INSERT INTO [dbo].[InMemoryStuff](InMemoryId, Created) VALUES ('Description' + CAST(@j as varchar), GETDATE());
    set @j = @j + 1;
end

-- grab a baseline of virtual file stats to be diff'd later
select f.num_of_reads, f.num_of_bytes_read
into #dm_io_virtual_file_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id('LogFileRead_Test') and file_id = FILE_IDEX('LogFileRead_Test_log');

-- hands off my log file, CHECKDB!
GO
DBCC CHECKDB ([LogFileRead_Test]) WITH NO_INFOMSGS, ALL_ERRORMSGS, DATA_PURITY;

-- grab the latest virtual file stats, and compare with the previous capture
GO
select f.num_of_reads, f.num_of_bytes_read
into #checkdb_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id('LogFileRead_Test') and file_id = FILE_IDEX('LogFileRead_Test_log');

select 
        collection_time = GETDATE() 
        , num_of_reads = - f.num_of_reads + t.num_of_reads
        , num_of_bytes_read = - f.num_of_bytes_read + t.num_of_bytes_read
into #dm_io_virtual_file_stats_diff
from #dm_io_virtual_file_stats f, #checkdb_stats t;

drop table #checkdb_stats;
drop table #dm_io_virtual_file_stats;

-- CHECKDB ignored my comment
select collection_time, num_of_reads, num_of_bytes_read
from #dm_io_virtual_file_stats_diff d
order by d.collection_time;

drop table #dm_io_virtual_file_stats_diff;

-- I was *not* raised in a barn
USE [master];

ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];

Vì repro này thường chỉ tạo ra 1 hoặc 106 tệp nhật ký đọc, tôi nghĩ rằng tôi sẽ đào sâu vào 1 với một phiên sự kiện mở rộng file_read và file_read_completed.

name                timestamp                   mode        offset  database_id file_id size    duration
file_read           2018-04-06 10:51:11.1098141 Contiguous  72704   9           2       0       NULL    
file_read_completed 2018-04-06 10:51:11.1113345 Contiguous  72704   9           2       491520  1       

Và đây là chi tiết VLF ( DBCC LOGINFO()) cho ngữ cảnh trên các độ lệch đó và như vậy:

RecoveryUnitId  FileId  FileSize    StartOffset FSeqNo  Status  Parity  CreateLSN
0               2       2031616     8192        34      2       64      0
0               2       2031616     2039808     35      2       64      0
0               2       2031616     4071424     36      2       64      0
0               2       2285568     6103040     37      2       64      0
0               2       15728640    8388608     38      2       64      34000000005200001
0               2       15728640    24117248    39      2       64      34000000005200001
0               2       15728640    39845888    40      2       64      34000000005200001
0               2       15728640    55574528    0       0       0       34000000005200001
0               2       15728640    71303168    0       0       0       34000000005200001
0               2       15728640    87031808    0       0       0       34000000005200001
0               2       15728640    102760448   0       0       0       34000000005200001
0               2       15728640    118489088   0       0       0       34000000005200001

Vì vậy, hoạt động CHECKDB:

  • bắt đầu đọc 63 KB (64,512 byte) vào VLF đầu tiên,
  • đọc 480 KB (491,520 byte) và
  • đã không đọc 1441 KB cuối cùng (1.485.584 byte) của VLF

Tôi cũng đã nắm bắt được các cửa hàng, trong trường hợp chúng hữu ích.

file_read callstack:

(00007ffd`999a0860)   sqlmin!XeSqlPkg::file_read::Publish+0x1dc   |  (00007ffd`999a0b40)   sqlmin!XeSqlPkg::file_read_enqueued::Publish
(00007ffd`9a825e30)   sqlmin!FireReadEvent+0x118   |  (00007ffd`9a825f60)   sqlmin!FireReadEnqueuedEvent
(00007ffd`9980b500)   sqlmin!FCB::AsyncRead+0x74d   |  (00007ffd`9980b800)   sqlmin!FCB::AsyncReadInternal
(00007ffd`9970e9d0)   sqlmin!SQLServerLogMgr::LogBlockReadAheadAsync+0x6a6   |  (00007ffd`9970ec00)   sqlmin!LBH::Destuff
(00007ffd`9970a6d0)   sqlmin!LogConsumer::GetNextLogBlock+0x1591   |  (00007ffd`9970ab70)   sqlmin!LogPoolPrivateCacheBufferMgr::Lookup
(00007ffd`9a9fcbd0)   sqlmin!SQLServerLogIterForward::GetNext+0x258   |  (00007ffd`9a9fd2d0)   sqlmin!SQLServerLogIterForward::GetNextBlock
(00007ffd`9aa417f0)   sqlmin!SQLServerCOWLogIterForward::GetNext+0x2b   |  (00007ffd`9aa418c0)   sqlmin!SQLServerCOWLogIterForward::StartScan
(00007ffd`9aa64210)   sqlmin!RecoveryMgr::AnalysisPass+0x83b   |  (00007ffd`9aa65100)   sqlmin!RecoveryMgr::AnalyzeLogRecord
(00007ffd`9aa5ed50)   sqlmin!RecoveryMgr::PhysicalRedo+0x233   |  (00007ffd`9aa5f790)   sqlmin!RecoveryMgr::PhysicalCompletion
(00007ffd`9aa7fd90)   sqlmin!RecoveryUnit::PhysicalRecovery+0x358   |  (00007ffd`9aa802c0)   sqlmin!RecoveryUnit::CompletePhysical
(00007ffd`9a538b90)   sqlmin!StartupCoordinator::NotifyPhaseStart+0x3a   |  (00007ffd`9a538bf0)   sqlmin!StartupCoordinator::NotifyPhaseEnd
(00007ffd`9a80c430)   sqlmin!DBTABLE::ReplicaCreateStartup+0x2f4   |  (00007ffd`9a80c820)   sqlmin!DBTABLE::RefreshPostRecovery
(00007ffd`9a7ed0b0)   sqlmin!DBMgr::SyncAndLinkReplicaRecoveryPhase+0x890   |  (00007ffd`9a7edff0)   sqlmin!DBMgr::DetachDB
(00007ffd`9a7f2cd0)   sqlmin!DBMgr::CreatePhasedTransientReplica+0x869   |  (00007ffd`9a7f3630)   sqlmin!DBMgr::StrandTransientReplica
(00007ffd`9a7f2ae0)   sqlmin!DBMgr::CreateTransientReplica+0x118   |  (00007ffd`9a7f2cd0)   sqlmin!DBMgr::CreatePhasedTransientReplica
(00007ffd`99ec6d30)   sqlmin!DBDDLAgent::CreateReplica+0x1b5   |  (00007ffd`99ec6f90)   sqlmin!FSystemDatabase
(00007ffd`9abaaeb0)   sqlmin!UtilDbccCreateReplica+0x82   |  (00007ffd`9abab000)   sqlmin!UtilDbccDestroyReplica
(00007ffd`9ab0d7e0)   sqlmin!UtilDbccCheckDatabase+0x994   |  (00007ffd`9ab0ffd0)   sqlmin!UtilDbccRetainReplica
(00007ffd`9ab0cfc0)   sqlmin!DbccCheckDB+0x22d   |  (00007ffd`9ab0d380)   sqlmin!DbccCheckFilegroup
(00007ffd`777379c0)   sqllang!DbccCommand::Execute+0x193   |  (00007ffd`77737d70)   sqllang!DbccHelp
(00007ffd`777e58d0)   sqllang!CStmtDbcc::XretExecute+0x889   |  (00007ffd`777e6250)   sqllang!UtilDbccSetPermissionFailure
(00007ffd`76b02eb0)   sqllang!CMsqlExecContext::ExecuteStmts<1,1>+0x40d   |  (00007ffd`76b03410)   sqllang!CSQLSource::CleanupCompileXactState
(00007ffd`76b03a60)   sqllang!CMsqlExecContext::FExecute+0xa9e   |  (00007ffd`76b043d0)   sqllang!CCacheObject::Release
(00007ffd`76b03430)   sqllang!CSQLSource::Execute+0x981   |  (00007ffd`76b039b0)   sqllang!CSQLLock::Cleanup

file_read_completed callstack:

(00007ffd`99995cc0)   sqlmin!XeSqlPkg::file_read_completed::Publish+0x1fc   |  (00007ffd`99995fe0)   sqlmin!XeSqlPkg::file_write_completed::Publish
(00007ffd`9a826630)   sqlmin!FireIoCompletionEventLong+0x227   |  (00007ffd`9a8269c0)   sqlmin!IoRequestDispenser::Dump
(00007ffd`9969bee0)   sqlmin!FCB::IoCompletion+0x8e   |  (00007ffd`9969c180)   sqlmin!IoRequestDispenser::Put
(00007ffd`beaa11e0)   sqldk!IOQueue::CheckForIOCompletion+0x426   |  (00007ffd`beaa1240)   sqldk!SystemThread::GetCurrentId
(00007ffd`beaa15b0)   sqldk!SOS_Scheduler::SwitchContext+0x173   |  (00007ffd`beaa18a0)   sqldk!SOS_Scheduler::Switch
(00007ffd`beaa1d00)   sqldk!SOS_Scheduler::SuspendNonPreemptive+0xd3   |  (00007ffd`beaa1db0)   sqldk!SOS_Scheduler::ResumeNoCuzz
(00007ffd`99641720)   sqlmin!EventInternal<SuspendQueueSLock>::Wait+0x1e7   |  (00007ffd`99641ae0)   sqlmin!SOS_DispatcherPool<DispatcherWorkItem,DispatcherWorkItem,SOS_DispatcherQueue<DispatcherWorkItem,0,DispatcherWorkItem>,DispatcherPoolConfig,void * __ptr64>::GetDispatchers
(00007ffd`9aa437c0)   sqlmin!SQLServerLogMgr::CheckLogBlockReadComplete+0x1e6   |  (00007ffd`9aa44670)   sqlmin!SQLServerLogMgr::ValidateBlock
(00007ffd`9970a6d0)   sqlmin!LogConsumer::GetNextLogBlock+0x1b37   |  (00007ffd`9970ab70)   sqlmin!LogPoolPrivateCacheBufferMgr::Lookup
(00007ffd`9a9fcbd0)   sqlmin!SQLServerLogIterForward::GetNext+0x258   |  (00007ffd`9a9fd2d0)   sqlmin!SQLServerLogIterForward::GetNextBlock
(00007ffd`9aa417f0)   sqlmin!SQLServerCOWLogIterForward::GetNext+0x2b   |  (00007ffd`9aa418c0)   sqlmin!SQLServerCOWLogIterForward::StartScan
(00007ffd`9aa64210)   sqlmin!RecoveryMgr::AnalysisPass+0x83b   |  (00007ffd`9aa65100)   sqlmin!RecoveryMgr::AnalyzeLogRecord
(00007ffd`9aa5ed50)   sqlmin!RecoveryMgr::PhysicalRedo+0x233   |  (00007ffd`9aa5f790)   sqlmin!RecoveryMgr::PhysicalCompletion
(00007ffd`9aa7fd90)   sqlmin!RecoveryUnit::PhysicalRecovery+0x358   |  (00007ffd`9aa802c0)   sqlmin!RecoveryUnit::CompletePhysical
(00007ffd`9a538b90)   sqlmin!StartupCoordinator::NotifyPhaseStart+0x3a   |  (00007ffd`9a538bf0)   sqlmin!StartupCoordinator::NotifyPhaseEnd
(00007ffd`9a80c430)   sqlmin!DBTABLE::ReplicaCreateStartup+0x2f4   |  (00007ffd`9a80c820)   sqlmin!DBTABLE::RefreshPostRecovery
(00007ffd`9a7ed0b0)   sqlmin!DBMgr::SyncAndLinkReplicaRecoveryPhase+0x890   |  (00007ffd`9a7edff0)   sqlmin!DBMgr::DetachDB
(00007ffd`9a7f2cd0)   sqlmin!DBMgr::CreatePhasedTransientReplica+0x869   |  (00007ffd`9a7f3630)   sqlmin!DBMgr::StrandTransientReplica
(00007ffd`9a7f2ae0)   sqlmin!DBMgr::CreateTransientReplica+0x118   |  (00007ffd`9a7f2cd0)   sqlmin!DBMgr::CreatePhasedTransientReplica
(00007ffd`99ec6d30)   sqlmin!DBDDLAgent::CreateReplica+0x1b5   |  (00007ffd`99ec6f90)   sqlmin!FSystemDatabase
(00007ffd`9abaaeb0)   sqlmin!UtilDbccCreateReplica+0x82   |  (00007ffd`9abab000)   sqlmin!UtilDbccDestroyReplica
(00007ffd`9ab0d7e0)   sqlmin!UtilDbccCheckDatabase+0x994   |  (00007ffd`9ab0ffd0)   sqlmin!UtilDbccRetainReplica
(00007ffd`9ab0cfc0)   sqlmin!DbccCheckDB+0x22d   |  (00007ffd`9ab0d380)   sqlmin!DbccCheckFilegroup
(00007ffd`777379c0)   sqllang!DbccCommand::Execute+0x193   |  (00007ffd`77737d70)   sqllang!DbccHelp

Các stacktraces này tương quan với câu trả lời của Max chỉ ra rằng CHECKDB đang sử dụng ảnh chụp nhanh bên trong bất chấp sự hiện diện của các bảng Hekaton.

Tôi đã đọc rằng ảnh chụp nhanh thực hiện khôi phục để hoàn tác các giao dịch không được cam kết :

Các giao dịch không được cam kết được khôi phục trong ảnh chụp nhanh cơ sở dữ liệu mới được tạo bởi vì Cơ sở dữ liệu chạy phục hồi sau khi ảnh chụp nhanh được tạo (các giao dịch trong cơ sở dữ liệu không bị ảnh hưởng).

Nhưng điều này vẫn không giải thích được tại sao một đoạn lớn tệp nhật ký thường được đọc trong kịch bản sản xuất của tôi (và đôi khi trong bản repro được cung cấp ở đây). Tôi không nghĩ rằng tôi có nhiều giao dịch trên chuyến bay tại một thời điểm nhất định trong ứng dụng của mình và chắc chắn không có bất kỳ giao dịch nào ở đây.

Câu trả lời:


10

Mặc dù tài liệu SQL Server tuyên bố rằng các cơ sở dữ liệu có bảng "Trong bộ nhớ" không hỗ trợ ảnh chụp nhanh, ảnh chụp nhanh "nội bộ" cần thiết DBCC CHECKDBvẫn có thể được tạo do thao tác checkdb không chạm vào bảng trong bộ nhớ và ảnh chụp nhanh chỉ chụp thay đổi vào các bảng trên đĩa.

Có lẽ, Microsoft đã chọn ngăn các ảnh chụp nhanh do người dùng tạo trên cơ sở dữ liệu bằng các bảng trong Bộ nhớ vì họ sẽ cần sao chép các cấu trúc trong bộ nhớ để ảnh chụp nhanh thực sự là một ảnh chụp nhanh hoàn toàn theo nghĩa thông thường, lấy người dùng làm trung tâm. Sao chép các bảng trong bộ nhớ để chụp nhanh có thể dễ dàng khiến máy chủ hết bộ nhớ, đây không phải là điều tốt

Bạn có thể tự chứng minh rằng một ảnh chụp nhanh DBCC nội bộ đang được tạo bằng cách xem thư mục dữ liệu nơi lưu trữ cơ sở dữ liệu chính trong khi chạy DBCC CHECKDB. Nếu một ảnh chụp nhanh nội bộ được tạo, bạn sẽ thấy một tệp có tên LogFileRead_Test.mdf_MSSQL_DBCC7( 7có thể khác - nó đại diện cho id cơ sở dữ liệu cho cơ sở dữ liệu của bạn).

Khi tệp ảnh chụp nhanh đã được tạo, SQL Server cần chạy recovery trên cơ sở dữ liệu để đưa nó vào trạng thái nhất quán cần thiết cho DBCC CHECKDB để chạy. Bất kỳ hành động đọc nhật ký nào bạn thấy có thể là kết quả của quá trình khôi phục đó. Tôi đã xây dựng một giàn khoan nhanh để kiểm tra đầu ra của nhiều DBCC CHECKDBhành động, điều này chứng tỏ rằng nếu không có giao dịch giữa các séc, thì không có tệp nhật ký nào được đọc.

USE master;
SET IMPLICIT_TRANSACTIONS OFF;
USE [master];
IF (DB_ID(N'LogFileRead_Test') IS NOT NULL) 
BEGIN
    ALTER DATABASE [LogFileRead_Test]
    SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [LogFileRead_Test];
END

CREATE DATABASE [LogFileRead_Test]
ALTER DATABASE [LogFileRead_Test]
MODIFY FILE
(
    NAME = LogFileRead_Test_log,
    SIZE = 128MB
);

ALTER DATABASE [LogFileRead_Test]
ADD FILEGROUP [LatencyTestInMemoryFileGroup] CONTAINS MEMORY_OPTIMIZED_DATA;
ALTER DATABASE [LogFileRead_Test]
ADD FILE 
(
    NAME = [LatencyTestInMemoryFile], 
    FILENAME = 'C:\temp\LogFileRead_Test_SessionStateInMemoryFile'
) TO FILEGROUP [LatencyTestInMemoryFileGroup];
GO

USE LogFileRead_Test;

CREATE TABLE [dbo].[InMemoryStuff] (
    [InMemoryId]   NVARCHAR (88)    COLLATE Latin1_General_100_BIN2 NOT NULL,
    [Created]     DATETIME2 (7)    NOT NULL,
    CONSTRAINT [PK_InMemoryStuff_InMemoryId] 
    PRIMARY KEY NONCLUSTERED 
    HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240)
)
WITH (MEMORY_OPTIMIZED = ON);

;WITH src AS (
    SELECT n.Num
    FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))n(Num)
)
INSERT INTO [dbo].[InMemoryStuff] (InMemoryId, Created) 
SELECT 'Description' + CONVERT(varchar(30)
        , ((s1.Num * 10000) 
         + (s2.Num * 1000) 
         + (s3.Num * 100) 
         + (s4.Num * 10) 
         + (s5.Num)))
    , GETDATE()
FROM src s1
    CROSS JOIN src s2
    CROSS JOIN src s3
    CROSS JOIN src s4
    CROSS JOIN src s5;
USE master;

DECLARE @cmd nvarchar(max);
DECLARE @msg nvarchar(1000);
DECLARE @l int;
DECLARE @m int;
SET @m = 10;
SET @l = 1;
IF OBJECT_ID(N'tempdb..#vfs', N'U') IS NOT NULL DROP TABLE #vfs;
CREATE TABLE #vfs (
    vfs_run int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , collection_time datetime2(7)
    , num_of_reads bigint
    , num_of_bytes_read bigint
);

WHILE @l <= @m 
BEGIN
SET @msg = N'loop ' + CONVERT(nvarchar(10), @l);
RAISERROR (@msg, 0, 1) WITH NOWAIT;

SET @cmd = 'USE [LogFileRead_Test];
-- grab a baseline of virtual file stats to be diff''d later
select f.num_of_reads, f.num_of_bytes_read
into #dm_io_virtual_file_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id(''LogFileRead_Test'') and file_id = FILE_IDEX(''LogFileRead_Test_log'');

DBCC CHECKDB ([LogFileRead_Test]) WITH NO_INFOMSGS, ALL_ERRORMSGS, DATA_PURITY;

-- grab the latest virtual file stats, and compare with the previous capture
select f.num_of_reads, f.num_of_bytes_read
into #checkdb_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id(''LogFileRead_Test'') and file_id = FILE_IDEX(''LogFileRead_Test_log'');

select 
        collection_time = GETDATE() 
        , num_of_reads = - f.num_of_reads + t.num_of_reads
        , num_of_bytes_read = - f.num_of_bytes_read + t.num_of_bytes_read
into #dm_io_virtual_file_stats_diff
from #dm_io_virtual_file_stats f, #checkdb_stats t;

--drop table #checkdb_stats;
--drop table #dm_io_virtual_file_stats;

-- CHECKDB ignored my comment
select collection_time, num_of_reads, num_of_bytes_read
from #dm_io_virtual_file_stats_diff d
order by d.collection_time;

--drop table #dm_io_virtual_file_stats_diff;
';
INSERT INTO #vfs (collection_time, num_of_reads, num_of_bytes_read)
EXEC sys.sp_executesql @cmd;

SET @l += 1;
END

USE master;
SET @cmd = 'USE [master];
ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];
';
EXEC sys.sp_executesql @cmd;

SELECT *
FROM #vfs
ORDER BY vfs_run;

Kết quả:

╔═════════╦═════════════════════════════╦═════════ ═════╦═══════════════════╗
Vfs_run ║ bộ sưu tập_time ║ num_of_reads ║ num_of_bytes_read
╠═════════╬═════════════════════════════╬═════════ ═════╬═══════════════════╣
1 2018-04-06 15: 53: 37,6566667 1 ║ 491520
2 ║ 2018-04-06 15: 53: 37.8300000 0 ║ 0
3 ║ 2018-04-06 15: 53: 38.0166667 0 0
4 ║ 2018-04-06 15: 53: 38,1866667 0 0
5 ║ 2018-04-06 15: 53: 38.3766667 0 0
6 ║ 2018-04-06 15: 53: 38.5633333 0 0
7 2018-04-06 15: 53: 38.7333333 0 ║ 0
8 ║ 2018-04-06 15: 53: 38.9066667 0 0
9 ║ 2018-04-06 15: 53: 39,0933333 ║ 0 0
10 ║ 2018-04-06 15: 53: 39.2800000 0 0
╚═════════╩═════════════════════════════╩═════════ ═════╩═══════════════════╝

Ngoài ra, thay vì sử dụng cách tiếp cận RBAR để chèn dữ liệu vào bảng thử nghiệm, bạn có thể muốn sử dụng cách tiếp cận dựa trên tập hợp đơn giản, chẳng hạn như cách tiếp cận dưới đây:

;WITH src AS (
    SELECT n.Num
    FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))n(Num)
)
INSERT INTO [dbo].[InMemoryStuff] (InMemoryId, Created) 
SELECT 'Description' + CONVERT(varchar(30)
     , ((s1.Num * 10000) 
      + (s2.Num * 1000) 
      + (s3.Num * 100) 
      + (s4.Num * 10) 
      + (s5.Num)))
    , GETDATE()
FROM src s1
    CROSS JOIN src s2
    CROSS JOIN src s3
    CROSS JOIN src s4
    CROSS JOIN src s5;

Trong thử nghiệm của tôi, nó lấp đầy bảng dưới 3 giây, trong khi phương pháp RBAR mất nhiều thời gian . Ngoài ra, bình luận tốt đẹp trong mã của bạn, làm cho tôi lol.

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.