SELECT 쿼리로 인해 쓰기가 발생하는 이유는 무엇입니까?


34

SQL Server 2016 SP1 CU6을 실행하는 서버에서 확장 이벤트 세션에 쓰기를 유발하는 SELECT 쿼리가 표시되는 경우가 있습니다. 예를 들면 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

실행 계획은 해시 테이블, 스풀 또는 정렬 등 TempDB에 유출 될 수있는 쓰기의 명백한 원인을 보여주지 않습니다.

여기에 이미지 설명을 입력하십시오

MAX 유형 또는 자동 통계 업데이트에 변수를 할당하면이 문제가 발생할 수 있지만이 경우 쓰기의 원인이 아닙니다.

그 글에서 무엇을 얻을 수 있습니까?

답변:


8

모양 없는

원래 답변에 이것들을 포함했는지 기억이 나지 않았 으므로 여기에 다른 커플이 있습니다.

스풀!

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

38

경우에 따라 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);

로그 항목에는 세 개의 중첩 트랜잭션이 표시되지만 커밋 레코드는 두 개만 표시됩니다. 프로덕션 환경에서도 이와 유사한 상황에서, 암시 적 트랜잭션을 사용하여 예기치 않게 쓰기 트랜잭션을 시작하여 트랜잭션 로그가 지워지지 않는 클라이언트 라이브러리의 결함이있을 수 있습니다. 라이브러리는 업데이트, 삽입 또는 삭제 명령문을 실행 한 후에 만 ​​커미트를 발행하도록 작성되었으므로 커미트 명령을 발행하지 않고 쓰기 트랜잭션을 열린 상태로 두었습니다.


25

이 문제가 발생할 수있는 또 다른 시간이 있으며 자동 통계 업데이트가 적용됩니다.

우리가 살펴볼 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 세션의 흥미로운 결과 중 일부 :

견과류

자동 통계 업데이트에는 쓰기가 표시되지 않지만 통계 업데이트 직후 쿼리에 하나의 쓰기가 표시됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.