답변:
원래 답변에 이것들을 포함했는지 기억이 나지 않았 으므로 여기에 다른 커플이 있습니다.
SQL Server에는 tempdb에 저장된 임시 데이터 구조 인 다양한 스풀이 많이 있습니다. 두 가지 예는 테이블 및 인덱스 스풀입니다.
조회 계획에서 발생하면 해당 스풀에 대한 쓰기가 조회와 연관됩니다.
이것들은 DMV, 프로파일 러, XE 등에 쓰기로 등록됩니다.
수행 된 쓰기의 양은 스풀링 된 데이터 크기에 따라 분명히 증가합니다.
SQL Server가 특정 연산자를위한 충분한 메모리를 얻지 못하면 일부 페이지를 디스크에 쏟을 수 있습니다. 이것은 주로 정렬과 해시에서 발생합니다. 실제 실행 계획에서 볼 수 있으며 최신 버전의 SQL Server에서는 유출이 dm_exec_query_stats 에서 추적됩니다 .
SELECT deqs.sql_handle,
deqs.total_spills,
deqs.last_spills,
deqs.min_spills,
deqs.max_spills
FROM sys.dm_exec_query_stats AS deqs
WHERE deqs.min_spills > 0;
위에서 사용한 것과 비슷한 XE 세션을 자신의 데모에서 볼 수 있습니다.
CREATE EVENT SESSION spools_and_spills
ON SERVER
ADD EVENT sqlserver.sql_batch_completed
( ACTION ( sqlserver.sql_text ))
ADD TARGET package0.event_file
( SET filename = N'c:\temp\spools_and_spills' )
WITH ( MAX_MEMORY = 4096KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 1 SECONDS,
MAX_EVENT_SIZE = 0KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF );
GO
경우에 따라 Query Store는 select 문의 영향으로 동일한 세션에서 쓰기가 발생할 수 있습니다.
다음과 같이 재현 할 수 있습니다.
USE master;
GO
CREATE DATABASE [Foo];
ALTER DATABASE [Foo] SET QUERY_STORE (OPERATION_MODE = READ_WRITE,
CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
DATA_FLUSH_INTERVAL_SECONDS = 900,
INTERVAL_LENGTH_MINUTES = 60,
MAX_STORAGE_SIZE_MB = 100,
QUERY_CAPTURE_MODE = ALL,
SIZE_BASED_CLEANUP_MODE = AUTO);
USE Foo;
CREATE TABLE Test (a int, b nvarchar(max));
INSERT INTO Test SELECT 1, 'string';
모니터링을위한 확장 이벤트 세션을 작성하십시오.
CREATE EVENT SESSION [Foo] ON SERVER
ADD EVENT sqlserver.rpc_completed(SET collect_data_stream=(1)
ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
WHERE ([writes]>(0))),
ADD EVENT sqlserver.sql_batch_completed(SET collect_batch_text=(1)
ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
WHERE ([writes]>(0)))
ADD TARGET package0.event_file(SET filename=N'C:\temp\FooActivity2016.xel',max_file_size=(11),max_rollover_files=(999999))
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_MULTIPLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF);
다음을 실행하십시오.
WHILE @@TRANCOUNT > 0 COMMIT
SET IMPLICIT_TRANSACTIONS ON;
SET NOCOUNT ON;
GO
DECLARE @b nvarchar(max);
SELECT @b = b FROM dbo.Test WHERE a = 1;
WAITFOR DELAY '00:00:01.000';
GO 86400
이를 재현하기 위해 암시 적 트랜잭션이 필요할 수도 있고 아닐 수도 있습니다.
기본적으로 다음 1 시간 동안 쿼리 저장소의 통계 수집 작업은 데이터를 기록합니다. 이것은 (때로는?) 시간 동안 실행 된 첫 번째 사용자 쿼리의 일부로 나타납니다. 확장 이벤트 세션에는 다음과 유사한 내용이 표시됩니다.
트랜잭션 로그에는 발생한 쓰기가 표시됩니다.
USE Foo;
SELECT [Transaction ID], [Begin Time], SPID, Operation,
[Description], [Page ID], [Slot ID], [Parent Transaction ID]
FROM sys.fn_dblog(null,null)
/* Adjust based on contents of your transaction log */
WHERE [Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
OR [Parent Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
ORDER BY [Current LSN];
페이지를 검사하면 DBCC PAGE
글이임을 나타냅니다 sys.plan_persist_runtime_stats_interval
.
USE Foo;
DBCC TRACEON(3604);
DBCC PAGE(5,1,344,1); SELECT
OBJECT_NAME(229575856);
로그 항목에는 세 개의 중첩 트랜잭션이 표시되지만 커밋 레코드는 두 개만 표시됩니다. 프로덕션 환경에서도 이와 유사한 상황에서, 암시 적 트랜잭션을 사용하여 예기치 않게 쓰기 트랜잭션을 시작하여 트랜잭션 로그가 지워지지 않는 클라이언트 라이브러리의 결함이있을 수 있습니다. 라이브러리는 업데이트, 삽입 또는 삭제 명령문을 실행 한 후에 만 커미트를 발행하도록 작성되었으므로 커미트 명령을 발행하지 않고 쓰기 트랜잭션을 열린 상태로 두었습니다.
이 문제가 발생할 수있는 또 다른 시간이 있으며 자동 통계 업데이트가 적용됩니다.
우리가 살펴볼 XE 세션은 다음과 같습니다.
CREATE EVENT SESSION batches_and_stats
ON SERVER
ADD EVENT sqlserver.auto_stats
( ACTION ( sqlserver.sql_text )),
ADD EVENT sqlserver.sql_batch_completed
( ACTION ( sqlserver.sql_text ))
ADD TARGET package0.event_file
( SET filename = N'c:\temp\batches_and_stats' )
WITH ( MAX_MEMORY = 4096KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF );
GO
그런 다음이 정보를 사용하여 정보를 수집합니다.
USE tempdb
DROP TABLE IF EXISTS dbo.SkewedUp
CREATE TABLE dbo.SkewedUp (Id INT NOT NULL, INDEX cx_su CLUSTERED (Id))
INSERT dbo.SkewedUp WITH ( TABLOCK ) ( Id )
SELECT CASE WHEN x.r % 15 = 0 THEN 1
WHEN x.r % 5 = 0 THEN 1000
WHEN x.r % 3 = 0 THEN 10000
ELSE 100000
END AS Id
FROM ( SELECT TOP 1000000 ROW_NUMBER() OVER ( ORDER BY @@DBTS ) AS r
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2 ) AS x;
ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = START
SELECT su.Id, COUNT(*) AS records
FROM dbo.SkewedUp AS su
WHERE su.Id > 0
GROUP BY su.Id
ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = STOP
XE 세션의 흥미로운 결과 중 일부 :
자동 통계 업데이트에는 쓰기가 표시되지 않지만 통계 업데이트 직후 쿼리에 하나의 쓰기가 표시됩니다.