다음을 통해 공유


전용 SQL 풀에서 느린 쿼리 문제 해결

적용 대상: Azure Synapse 분석

이 문서는 이유를 식별하고 Azure Synapse Analytics 전용 SQL 풀에서 쿼리와 관련된 일반적인 성능 문제에 대한 완화를 적용하는 데 도움이 됩니다.

단계를 따라 문제를 해결하거나 Azure Data Studio를 통해 Notebook에서 단계를 실행합니다. 처음 세 단계에서는 쿼리의 수명 주기를 설명하는 원격 분석 수집을 안내합니다. 문서의 끝에 있는 참조는 수집된 데이터에 있는 잠재적인 기회를 분석하는 데 도움이 됩니다.

참고

이 Notebook을 열기 전에 Azure Data Studio가 로컬 컴퓨터에 설치되어 있는지 확인합니다. 설치하려면 Azure Data Studio 설치 방법 알아보기로 이동하세요.

중요

보고된 성능 문제의 대부분은 다음으로 인해 발생합니다.

  • 오래된 통계
  • 비정상 클러스터형 columnstore 인덱스(CCI)

문제 해결 시간을 절약하려면 통계가 생성되고 최신 이고 CCI가 다시 작성되었는지 확인합니다.

1단계: request_id 식별(예: QID)

request_id 느린 쿼리의 은 느린 쿼리에 대한 잠재적인 이유를 조사하는 데 필요합니다. 다음 스크립트를 문제 해결하려는 쿼리를 식별하기 위한 시작점으로 사용합니다. 느린 쿼리가 식별되면 값을 적어둡 request_id 니다.

-- Monitor active queries
SELECT *
FROM sys.dm_pdw_exec_requests
WHERE [status] NOT IN ('Completed','Failed','Cancelled')
AND session_id <> session_id()
-- AND [label] = '<YourLabel>'
-- AND resource_allocation_percentage is not NULL
ORDER BY submit_time DESC;

-- Find top 10 longest running queries
SELECT TOP 10 *
FROM sys.dm_pdw_exec_requests
ORDER BY total_elapsed_time DESC;

느린 쿼리의 대상을 더 잘 지정하려면 스크립트를 실행할 때 다음 팁을 사용합니다.

  • 또는 total_elapsed_time DESC 를 기준으로 submit_time DESC 정렬하여 결과 집합의 맨 위에 가장 오래 실행되는 쿼리를 표시합니다.

  • 쿼리에서 를 사용한 OPTION(LABEL='<YourLabel>') 다음 열을 필터링 label 하여 식별합니다.

  • 대상 문이 일괄 처리에 포함되어 있다는 것을 알고 있을 때 값이 resource_allocation_percentage 없는 모든 QID를 필터링하는 것이 좋습니다.

    참고: 이 필터는 다른 세션에서 차단되는 일부 쿼리를 필터링할 수도 있으므로 주의해야 합니다.

2단계: 쿼리에 시간이 걸리는 위치 확인

다음 스크립트를 실행하여 쿼리의 성능 문제를 일으킬 수 있는 단계를 찾습니다. 다음 표에 설명된 값으로 스크립트의 변수를 업데이트합니다. @ShowActiveOnly 분산 계획의 전체 그림을 보려면 값을 0으로 변경합니다. 결과 집합에서 식별된 StepIndex느린 단계의 , PhaseDescription 값을 기록해 둡다.

매개 변수 설명
@QID request_id 1단계에서 얻은 값
@ShowActiveOnly 0 - 쿼리에 대한 모든 단계 표시
1 - 현재 활성 단계만 표시
DECLARE @QID VARCHAR(16) = '<request_id>', @ShowActiveOnly BIT = 1; 
-- Retrieve session_id of QID
DECLARE @session_id VARCHAR(16) = (SELECT session_id FROM sys.dm_pdw_exec_requests WHERE request_id = @QID);
-- Blocked by Compilation or Resource Allocation (Concurrency)
SELECT @session_id AS session_id, @QID AS request_id, -1 AS [StepIndex], 'Compilation' AS [Phase],
   'Blocked waiting on '
       + MAX(CASE WHEN waiting.type = 'CompilationConcurrencyResourceType' THEN 'Compilation Concurrency'
                  WHEN waiting.type LIKE 'Shared-%' THEN ''
                  ELSE 'Resource Allocation (Concurrency)' END)
       + MAX(CASE WHEN waiting.type LIKE 'Shared-%' THEN ' for ' + REPLACE(waiting.type, 'Shared-', '')
             ELSE '' END) AS [Description],
   MAX(waiting.request_time) AS [StartTime], GETDATE() AS [EndTime],
   DATEDIFF(ms, MAX(waiting.request_time), GETDATE())/1000.0 AS [Duration],
   NULL AS [Status], NULL AS [EstimatedRowCount], NULL AS [ActualRowCount], NULL AS [TSQL]
FROM sys.dm_pdw_waits waiting
WHERE waiting.session_id = @session_id
   AND ([type] LIKE 'Shared-%' OR
      [type] in ('ConcurrencyResourceType', 'UserConcurrencyResourceType', 'CompilationConcurrencyResourceType'))
   AND [state] = 'Queued'
GROUP BY session_id 
-- Blocked by another query
UNION ALL
SELECT @session_id AS session_id, @QID AS request_id, -1 AS [StepIndex], 'Compilation' AS [Phase],
   'Blocked by ' + blocking.session_id + ':' + blocking.request_id + ' when requesting ' + waiting.type + ' on '
   + QUOTENAME(waiting.object_type) + waiting.object_name AS [Description],
   waiting.request_time AS [StartTime], GETDATE() AS [EndTime],
   DATEDIFF(ms, waiting.request_time, GETDATE())/1000.0 AS [Duration],
   NULL AS [Status], NULL AS [EstimatedRowCount], NULL AS [ActualRowCount],
   COALESCE(blocking_exec_request.command, blocking_exec_request.command2) AS [TSQL]
FROM sys.dm_pdw_waits waiting
   INNER JOIN sys.dm_pdw_waits blocking
      ON waiting.object_type = blocking.object_type
      AND waiting.object_name = blocking.object_name
   INNER JOIN sys.dm_pdw_exec_requests blocking_exec_request
      ON blocking.request_id = blocking_exec_request.request_id
WHERE waiting.session_id = @session_id AND waiting.state = 'Queued'
   AND blocking.state = 'Granted' AND waiting.type != 'Shared' 
-- Request Steps
UNION ALL
SELECT @session_id AS session_id, @QID AS request_id, step_index AS [StepIndex],
       'Execution' AS [Phase], operation_type + ' (' + location_type + ')' AS [Description],
       start_time AS [StartTime], end_time AS [EndTime],
       total_elapsed_time/1000.0 AS [Duration], [status] AS [Status],
       CASE WHEN estimated_rows > -1 THEN estimated_rows END AS [EstimatedRowCount],
       CASE WHEN row_count > -1 THEN row_count END AS [ActualRowCount],
       command AS [TSQL]
FROM sys.dm_pdw_request_steps
WHERE request_id = @QID
   AND [status] = CASE @ShowActiveOnly WHEN 1 THEN 'Running' ELSE [status] END
ORDER BY StepIndex;

3단계: 단계 세부 정보 검토

다음 스크립트를 실행하여 이전 단계에서 식별된 단계의 세부 정보를 검토합니다. 다음 표에 설명된 값으로 스크립트의 변수를 업데이트합니다. @ShowActiveOnly 값을 0으로 변경하여 모든 배포 타이밍을 비교합니다. 성능 문제를 일으킬 수 있는 배포 값을 기록해 둡 wait_type 니다.

매개 변수 설명
@QID request_id 1단계에서 얻은 값
@StepIndex StepIndex 2단계에서 식별된 값
@ShowActiveOnly 0 - 지정된 StepIndex 값에 대한 모든 분포 표시
1 - 지정된 StepIndex 값에 대해 현재 활성 분포만 표시
DECLARE @QID VARCHAR(16) = '<request_id>', @StepIndex INT = <StepIndex>, @ShowActiveOnly BIT = 1;
WITH dists
AS (SELECT request_id, step_index, 'sys.dm_pdw_sql_requests' AS source_dmv,
       distribution_id, pdw_node_id, spid, 'NativeSQL' AS [type], [status],
       start_time, end_time, total_elapsed_time, row_count
    FROM sys.dm_pdw_sql_requests
    WHERE request_id = @QID AND step_index = @StepIndex
    UNION ALL
    SELECT request_id, step_index, 'sys.dm_pdw_dms_workers' AS source_dmv,
       distribution_id, pdw_node_id, sql_spid AS spid, [type],
       [status], start_time, end_time, total_elapsed_time, rows_processed as row_count
    FROM sys.dm_pdw_dms_workers
    WHERE request_id = @QID AND step_index = @StepIndex
   )
SELECT sr.step_index, sr.distribution_id, sr.pdw_node_id, sr.spid,
       sr.type, sr.status, sr.start_time, sr.end_time,
       sr.total_elapsed_time, sr.row_count, owt.wait_type, owt.wait_time
FROM dists sr
   LEFT JOIN sys.dm_pdw_nodes_exec_requests owt
      ON sr.pdw_node_id = owt.pdw_node_id
         AND sr.spid = owt.session_id
         AND ((sr.source_dmv = 'sys.dm_pdw_sql_requests'
                 AND sr.status = 'Running') -- sys.dm_pdw_sql_requests status
              OR (sr.source_dmv = 'sys.dm_pdw_dms_requests'
                     AND sr.status not LIKE 'Step[CE]%')) -- sys.dm_pdw_dms_workers final statuses
WHERE sr.request_id = @QID
      AND ((sr.source_dmv = 'sys.dm_pdw_sql_requests' AND sr.status =
               CASE WHEN @ShowActiveOnly = 1 THEN 'Running' ELSE sr.status END)
           OR (sr.source_dmv = 'sys.dm_pdw_dms_workers' AND sr.status NOT LIKE
                  CASE WHEN @ShowActiveOnly = 1 THEN 'Step[CE]%' ELSE '' END))
      AND sr.step_index = @StepIndex
ORDER BY distribution_id

4단계: 진단 및 완화

컴파일 단계 문제

차단됨: 컴파일 동시성

동시성 컴파일 블록은 거의 발생하지 않습니다. 그러나 이러한 유형의 블록이 발생하면 대량의 쿼리가 짧은 시간에 제출되고 컴파일을 시작하기 위해 대기 중임을 나타냅니다.

완화

동시에 제출된 쿼리 수를 줄입니다.


차단됨: 리소스 할당

리소스 할당에 대해 차단된다는 것은 쿼리가 다음을 기반으로 실행되기를 기다리고 있음을 의미합니다.

  • 사용자와 연결된 리소스 클래스 또는 워크로드 그룹 할당에 따라 부여되는 메모리 양입니다.
  • 시스템 또는 워크로드 그룹에서 사용 가능한 메모리의 양입니다.
  • (선택 사항) 워크로드 그룹/분류자 중요도입니다.

완화

복잡한 쿼리 또는 이전 JOIN 구문

컴파일 단계에서 시간이 오래 걸리기 때문에 기본 쿼리 최적화 프로그램 메서드가 효과가 없는 것으로 입증되는 상황이 발생할 수 있습니다. 쿼리가 다음과 같은 경우 발생할 수 있습니다.

  • 많은 수의 조인 및/또는 하위 쿼리(복잡한 쿼리)를 포함합니다.
  • 절에서 FROM 조인자를 활용합니다(ANSI-92 스타일 조인이 아님).

이러한 시나리오는 비정형이지만 쿼리 최적화 프로그램이 계획을 선택하는 데 걸리는 시간을 줄이기 위해 기본 동작을 재정의하는 옵션이 있습니다.

완화

장기 실행 DROP TABLE 또는 TRUNCATE TABLE

실행 시간 효율성을 DROP TABLE 위해 및 TRUNCATE TABLE 문은 스토리지 정리를 백그라운드 프로세스로 연기합니다. 그러나 워크로드가 짧은 시간 프레임에 많은 수의 DROP/TRUNCATE TABLE 문을 수행하는 경우 메타데이터가 혼잡해지고 후속 DROP/TRUNCATE TABLE 문이 느리게 실행될 수 있습니다.

완화

유지 관리 기간을 식별하고, 모든 워크로드를 중지하고, DBCC SHRINKDATABASE 를 실행하여 이전에 삭제되거나 잘린 테이블을 즉시 정리합니다.


비정상 CCI(일반적으로)

CCI(클러스터형 columnstore 인덱스) 상태가 좋지 않으면 추가 메타데이터가 필요하므로 쿼리 최적화 프로그램이 최적의 계획을 결정하는 데 더 많은 시간이 걸릴 수 있습니다. 이 상황을 방지하려면 모든 CCI가 양수 상태인지 확인합니다.

완화

전용 SQL 풀에서 클러스터형 columnstore 인덱스 상태를 평가하고 수정합니다.


자동 만들기 통계 지연

자동 통계 만들기 옵션은AUTO_CREATE_STATISTICSON 기본적으로 쿼리 최적화 프로그램이 적절한 분산 계획 결정을 내릴 수 있도록 하는 데 도움이 됩니다. 그러나 자동 만들기 프로세스 자체는 초기 쿼리가 동일한 후속 실행보다 오래 걸릴 수 있습니다.

완화

쿼리의 첫 번째 실행에서 지속적으로 통계를 만들어야 하는 경우 쿼리를 실행하기 전에 통계를 수동으로 만들어야 합니다.


통계 자동 만들기 시간 제한

자동 통계 만들기 옵션은AUTO_CREATE_STATISTICSON 기본적으로 쿼리 최적화 프로그램이 적절한 분산 계획 결정을 내릴 수 있도록 하는 데 도움이 됩니다. 통계의 자동 생성은 SELECT 문에 대한 응답으로 발생하며 완료할 임계값은 5분입니다. 데이터 크기 및/또는 만들 통계 수가 5분 임계값보다 길어야 하는 경우 쿼리 실행을 계속할 수 있도록 통계 자동 생성이 중단됩니다. 통계를 만들지 못하면 쿼리 최적화 프로그램이 효율적인 분산 실행 계획을 생성하는 기능에 부정적인 영향을 주어 쿼리 성능이 저하될 수 있습니다.

완화

식별된 테이블/열에 대한 자동 만들기 기능을 사용하는 대신 통계를 수동으로 만듭니 다.

실행 단계 문제

  • 다음 표를 사용하여 2단계의 결과 집합을 분석합니다. 시나리오를 확인하고 자세한 정보 및 가능한 완화 단계에 대한 일반적인 원인을 검사.

    시나리오 일반적인 원인
    EstimatedRowCount / ActualRowCount < 25% 부정확한 추정치
    값은 Description 를 나타내 BroadcastMoveOperation 고 쿼리는 복제된 테이블을 참조합니다. 캐시되지 않은 복제 테이블
    1. @ShowActiveOnly = 0
    2. 높거나 예기치 않은 단계 수(step_index)가 관찰됩니다.
    3. 조인자 열의 데이터 형식은 테이블 간에 동일하지 않습니다.
    일치하지 않는 데이터 형식/크기
    1. 값은 Description 또는 HadoopShuffleOperationHadoopRoundRobinOperation 나타냅니다HadoopBroadcastOperation.
    2. total_elapsed_time 지정된 step_index 값이 실행 간에 일치하지 않습니다.
    임시 외부 테이블 쿼리
  • total_elapsed_time 3단계에서 얻은 값을 확인합니다. 지정된 단계의 몇 가지 배포에서 훨씬 더 높은 경우 다음 단계를 수행합니다.

    1. 각각에 대해 다음 명령을 실행하여 관련 필드에 참조된 TSQLstep_id 모든 테이블에 대한 데이터 분포를 확인합니다.

      DBCC PDW_SHOWSPACEUSED(<table>);
      
    2. 최소 행 값>/<최대 행 값>>이 0.1이면 <데이터 기울이기(저장됨)로 이동합니다.

    3. 그렇지 않으면 진행 중인 데이터 기울이기로 이동합니다.

부정확한 추정치

쿼리 최적화 프로그램이 최적의 계획을 생성하도록 통계를 최신 상태로 유지합니다. 예상 행 수가 실제 개수보다 훨씬 작으면 통계를 유지 관리해야 합니다.

완화

통계 만들기/업데이트


캐시되지 않은 복제 테이블

복제된 테이블을 만들었으며 복제된 테이블 캐시를 제대로 데우지 못하면 추가 데이터 이동 또는 최적이 되지 않은 분산 계획 생성으로 인해 예기치 않은 성능 저하가 발생합니다.

완화

  • DML 작업 후 복제된 캐시를 웜합니다.
  • DML 작업이 자주 있는 경우 테이블의 분포를 로 ROUND_ROBIN변경합니다.
일치하지 않는 데이터 형식/크기

테이블을 조인할 때 조인 열의 데이터 형식과 크기가 일치하는지 확인합니다. 그렇지 않으면 불필요한 데이터 이동으로 인해 CPU, IO 및 네트워크 트래픽의 가용성이 나머지 워크로드로 감소합니다.

완화

동일한 데이터 형식 및 크기가 없는 관련 테이블 열을 수정하도록 테이블을 다시 빌드합니다.


임시 외부 테이블 쿼리

외부 테이블에 대한 쿼리는 전용 SQL 풀에 데이터를 대량으로 로드하기 위해 설계되었습니다. 외부 테이블에 대한 임시 쿼리는 동시 스토리지 컨테이너 활동과 같은 외부 요인으로 인해 가변적인 기간을 겪을 수 있습니다.

완화

먼저 전용 SQL 풀에 데이터를 로드 한 다음 로드된 데이터를 쿼리합니다.


데이터 기울이기(저장됨)

데이터 기울이기란 데이터가 분산 간에 균등하게 분산되지 않음을 의미합니다. 분산 계획의 각 단계에서는 다음 단계로 이동하기 전에 모든 배포를 완료해야 합니다. 데이터가 기울어지면 CPU 및 IO와 같은 처리 리소스의 전체 잠재력을 달성할 수 없으므로 실행 시간이 느려집니다.

완화

분산 테이블에 대한 지침을 검토하여 보다 적절한 배포 열을 선택할 수 있도록 지원합니다.


기내 데이터 기울이기

기내 데이터 기울이기는 데이터 기울이기(저장된) 문제의 변형입니다. 그러나 왜곡된 디스크의 데이터 배포는 아닙니다. 특정 필터 또는 그룹화된 데이터에 대한 분산 계획의 특성으로 인해 형식 작업이 발생 ShuffleMoveOperation 합니다. 이 작업은 다운스트림에서 사용할 기울어진 출력을 생성합니다.

완화

  • 통계가 생성되고 최신 상태인지 확인합니다.
  • 더 높은 카디널리티 열로 이끌도록 열의 GROUP BY 순서를 변경합니다.
  • 조인이 여러 열을 포함하는 경우 다중 열 통계를 만듭니다.
  • 쿼리에 쿼리 힌트 OPTION(FORCE_ORDER) 를 추가합니다.
  • 쿼리를 리팩터링합니다.

대기 유형 문제

위의 일반적인 문제가 쿼리에 적용되지 않는 경우 3단계 데이터는 가장 오래 실행되는 단계에 대한 쿼리 처리를 방해하는 대기 유형(및 wait_timewait_type 경우)을 확인할 수 있습니다. 많은 수의 대기 유형이 있으며 유사한 완화로 인해 관련 범주로 그룹화됩니다. 다음 단계에 따라 쿼리 단계의 대기 범주를 찾습니다.

  1. wait_type 시간이 가장 많은 3단계에서 를 식별합니다.
  2. 대기 범주 매핑 테이블에서 대기 유형을 찾아 포함된 대기 범주를 식별합니다.
  3. 권장 완화를 위해 다음 목록에서 대기 범주와 관련된 섹션을 확장합니다.
컴파일

컴파일 범주의 대기 유형 문제를 완화하려면 다음 단계를 수행합니다.

  1. 문제가 있는 쿼리와 관련된 모든 개체에 대한 인덱스를 다시 작성합니다.
  2. 문제가 있는 쿼리와 관련된 모든 개체에 대한 통계를 업데이트합니다.
  3. 문제가 있는 쿼리를 다시 테스트하여 문제가 지속되는지 여부를 확인합니다.

문제가 지속되면 다음을 수행합니다.

  1. 다음을 사용하여 .sql 파일을 만듭니다.

    SET QUERY_DIAGNOSTICS ON; <Your_SQL>; SET QUERY_DIAGNOSTICS OFF;
    
  2. 명령 프롬프트 창을 열고 다음 명령을 실행합니다.

    sqlcmd −S <servername>.database.windows.net −d <databasename> −U <username> −G −I −i .\<sql_file_name>.sql −y0 −o .\<output_file_name>.txt
    
  3. 텍스트 편집기에서 output_file_name>.txt 엽니다<. 2단계에서 식별된 가장 긴 실행 단계에서 배포 수준 실행 계획(로 시작하는 <ShowPlanXML>줄)을 찾아 복사하여 확장이 .sqlplan인 별도의 텍스트 파일에 붙여넣습니다.

    참고: 분산 계획의 각 단계에서는 일반적으로 60개의 배포 수준 실행 계획을 기록합니다. 동일한 분산 계획 단계에서 실행 계획을 준비하고 비교하고 있는지 확인합니다.

  4. 3단계 쿼리는 다른 배포보다 훨씬 오래 걸리는 몇 가지 배포를 자주 보여 줍니다. SQL Server Management Studio 장기 실행 배포의 배포 수준 실행 계획(만든 .sqlplan 파일)을 빠르게 실행되는 배포와 비교하여 차이점의 잠재적 원인을 분석합니다.

Lock, Worker Thread
  • CCI 대신 행 저장소 인덱스 활용을 위해 자주 작은 변경이 발생하는 테이블을 변경하는 것이 좋습니다.
  • 변경 내용을 일괄 처리하고 덜 자주 더 많은 행으로 대상을 업데이트합니다.
버퍼 IO, 기타 디스크 IO, Tran 로그 IO

비정상 CCI

비정상 CCI는 IO, CPU 및 메모리 할당 증가에 기여하며, 이로 인해 쿼리 성능에 부정적인 영향을 줍니다. 이 문제를 완화하려면 다음 방법 중 하나를 시도해 보세요.

오래된 통계

통계가 오래되면 최적화되지 않은 분산 계획이 생성될 수 있으며, 여기에는 필요한 것보다 더 많은 데이터 이동이 수반됩니다. 불필요한 데이터 이동은 미사용 데이터뿐만 아니라 에서도 워크로드를 tempdb증가합니다. IO는 모든 쿼리에서 공유 리소스이므로 전체 워크로드에서 성능 영향을 느낄 수 있습니다.

이 상황을 해결하려면 모든 통계가 최신 상태이고 사용자 워크로드에 대한 업데이트를 유지하기 위한 유지 관리 계획이 마련되어 있는지 확인합니다.

무거운 IO 워크로드

전체 워크로드에서 많은 양의 데이터를 읽을 수 있습니다. Synapse 전용 SQL 풀은 DWU에 따라 리소스 크기를 조정합니다. 성능을 향상시키려면 다음 중 하나 또는 둘 다를 고려합니다.

CPU, 병렬 처리
시나리오 완화
가난한 CCI 건강 전용 SQL 풀에서 클러스터형 columnstore 인덱스 상태 평가 및 수정
사용자 쿼리에는 변환이 포함됩니다. 형식이 지정된 버전이 저장되도록 모든 서식 및 기타 변환 논리를 ETL 프로세스로 이동
워크로드의 우선 순위가 잘못 지정되었습니다. 워크로드 격리 구현
워크로드에 대한 DWU 부족 컴퓨팅 리소스를 늘리는 것이 좋습니다.

네트워크 IO

2단계에서 작업하는 동안 RETURN 문제가 발생하는 경우

  • 동시 병렬 프로세스 수를 줄입니다.
  • 영향을 가장 큰 프로세스를 다른 클라이언트로 스케일 아웃합니다.

다른 모든 데이터 이동 작업의 경우 네트워크 문제가 전용 SQL 풀 내부로 나타날 수 있습니다. 이 문제를 신속하게 완화하려면 다음 단계를 수행합니다.

  1. 전용 SQL 풀을 DW100c로 크기 조정
  2. 원하는 DWU 수준으로 다시 확장
SQL CLR

데이터를 변환하는 다른 방법(예CONVERT(): 스타일 포함)을 구현하여 함수를 자주 사용하지 FORMAT() 않도록 합니다.