Amazon Redshift의 쿼리 보고서와 쿼리 계획을 어떻게 연관시킬 수 있습니까?

최종 업데이트 날짜: 2020년 10월 28일

쿼리 계획을 Amazon Redshift 클러스터의 쿼리 보고서와 연관시키려고 합니다. 어떻게 해야 합니까?

간략한 설명

Amazon Redshift에서 쿼리를 실행하는 데 필요한 사용량을 확인하려면 EXPLAIN 명령을 사용합니다. EXPLAIN 명령은 실제로 쿼리를 실행하지 않고 쿼리 문에 대한 실행 계획을 표시합니다. 실행 계획은 관련된 쿼리 계획 및 실행 단계를 간략하게 설명합니다.

그런 다음 SVL_QUERY_REPORT 시스템 보기를 사용하여 클러스터 슬라이스 수준에서 쿼리 정보를 봅니다. 클러스터 전체에서 고르지 않은 데이터 분포를 감지하는 데 슬라이스 수준 정보를 사용할 수 있으므로 쿼리 성능에 영향을 줍니다.

주:  SVL_QUERY_REPORT에서  열은 클러스터 슬라이스당 처리되는 행 수를 나타냅니다. rows_pre_filter 열은 삭제 표시된 행을 필터링하기 전에 방출된 총 행 수를 나타냅니다.

Amazon Redshift는 쿼리 계획을 처리하고 계획을 단계, 세그먼트 및 스트림으로 변환합니다. 자세한 내용은 쿼리 계획 및 실행 워크플로를 참조하십시오.

해결 방법

테이블 만들기 및 쿼리에 대한 EXPLAIN 계획 및 SVL 쿼리 보고서 가져 오기

1.    정렬 키와 배포 키가 서로 다른 두 개의 테이블을 만듭니다.

2.    배포 키에 조인이 수행되지 않은 다음 쿼리를 실행합니다.

select eventname, sum (pricepaid) from sales, event where sales.eventid = event.eventid group by eventname order by 2 desc;

이 쿼리는 내부 테이블을 모든 컴퓨팅 노드에 배포합니다.

3.    쿼리 계획을 검색합니다.

EXPLAIN <query>;
                                               QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
 XN Merge  (cost=1002815368414.24..1002815368415.67 rows=571 width=27)
   Merge Key: sum(sales.pricepaid)
   ->  XN Network  (cost=1002815368414.24..1002815368415.67 rows=571 width=27)
         Send to leader
         ->  XN Sort  (cost=1002815368414.24..1002815368415.67 rows=571 width=27)
               Sort Key: sum(sales.pricepaid)
               ->  XN HashAggregate  (cost=2815368386.67..2815368388.10 rows=571 width=27)
                     ->  XN Hash Join DS_BCAST_INNER  (cost=109.98..2815367496.05 rows=178125 width=27)
                           Hash Cond: ("outer".eventid = "inner".eventid)
                           ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=14)
                           ->  XN Hash  (cost=87.98..87.98 rows=8798 width=21)
                                 ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=21)
(12 rows)

4.    다음 쿼리를 실행합니다.

select * from svl_query_report where query = <query_id> order by segment, step, elapsed_time, rows;

쿼리 보고서와 쿼리 계획 매핑

1.    다음 쿼리를 실행하여 svl_query_report를 가져옵니다.

select query,slice,segment,step,start_time,end_time,elapsed_time,rows,bytes,label from svl_query_report where query = 938787 and segment = 0 order by segment, step, elapsed_time, rows;

EXPLAIN <query>;
->  XN Hash  (cost=87.98..87.98 rows=8798 width=21)
   ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=21)

예제 출력은 다음과 같습니다.

 query  | slice | segment | step |         start_time         |         end_time          | elapsed_time | rows | bytes  |            label              
--------+-------+---------+------+----------------------------+---------------------------+--------------+------+--------+------------------------------
 938787 |     0 |       0 |    0 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4383 | 128626 | scan   tbl=278788 name=event 
 938787 |     1 |       0 |    0 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4415 | 128918 | scan   tbl=278788 name=event 
 938787 |     0 |       0 |    1 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4383 |      0 | project                      
 938787 |     1 |       0 |    1 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4415 |      0 | project                      
 938787 |     0 |       0 |    2 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4383 | 126660 | bcast                        
 938787 |     1 |       0 |    2 | 2020-05-22 11:11:48.828309 | 2020-05-22 11:11:48.82987 |         1561 | 4415 | 126920 | bcast                        

 (6 rows)

이 출력은 세그먼트 값이 0이면 Amazon Redshift가 순차 스캔 연산을 수행하여 이벤트 테이블을 스캔함을 나타냅니다.

2.    다음 쿼리를 실행하여 세그먼트 1의 쿼리 보고서를 가져옵니다.

select query,slice,segment,step,start_time,end_time,elapsed_time,rows,bytes,label from svl_query_report where query = 938787 and segment = 1 order by segment, step, elapsed_time, rows;

예제 출력은 다음과 같습니다.

query  | slice | segment | step |       start_time          |          end_time          | elapsed_time | rows | bytes  |     label           
--------+-------+---------+------+----------------------------+----------------------------+--------------+------+--------+-------------------------------------------
938787 |     1 |       1 |    0 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.830037 |         3173 |    0 |      0 | scan   tbl=376297 name=Internal Worktable 
 38787 |     0 |       1 |    0 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.831142 |         4278 | 8798 | 253580 | scan   tbl=376297 name=Internal Worktable 
938787 |     1 |       1 |    1 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.830037 |         3173 |    0 |      0 | project                                   
938787 |     0 |       1 |    1 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.831142 |         4278 | 8798 |      0 | project                                   
938787 |     1 |       1 |    2 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.830037 |         3173 |    0 |      0 | hash   tbl=439                            
938787 |     0 |       1 |    2 | 2020-05-22 11:11:48.826864 | 2020-05-22 11:11:48.831142 |         4278 | 8798 | 359156 | hash   tbl=439                            
(6 rows)

세그먼트 값이 1이고 조인의 내부 테이블에서 해시 테이블 연산이 수행될 때까지 쿼리가 계속 실행됩니다.

3.    세그먼트 값이 2인 쿼리에 대해 SVL_QUERY_REPORT를 가져오려면 다음 쿼리를 실행합니다.

EXPLAIN <query>;
->  XN Hash Join DS_BCAST_INNER  (cost=109.98..2815367496.05 rows=178125 width=27)
                           Hash Cond: ("outer".eventid = "inner".eventid)
   ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=14)

4.    다음 쿼리를 실행합니다.

select query,slice,segment,step,start_time,end_time,elapsed_time,rows,bytes,label from svl_query_report where query = 938787 and segment = 2 order by segment, step, elapsed_time, rows;

예제 출력은 다음과 같습니다.

  query  | slice | segment | step |         start_time         |          end_time          | elapsed_time | rows  |  bytes  |            label             
--------+-------+---------+------+----------------------------+----------------------------+--------------+-------+---------+------------------------------
 938787 |     1 |       2 |    0 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 | 1730380 | scan   tbl=278792 name=sales 
 938787 |     0 |       2 |    0 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 | 1718740 | scan   tbl=278792 name=sales 
 938787 |     1 |       2 |    1 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 |       0 | project                      
 938787 |     0 |       2 |    1 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 |       0 | project                      
 938787 |     1 |       2 |    2 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 |       0 | project                      
 938787 |     0 |       2 |    2 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 |       0 | project                      
 938787 |     1 |       2 |    3 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 |       0 | hjoin  tbl=439               
 938787 |     0 |       2 |    3 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 |       0 | hjoin  tbl=439               
 938787 |     1 |       2 |    4 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 |       0 | project                      
 938787 |     0 |       2 |    4 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 |       0 | project                      
 938787 |     1 |       2 |    5 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 | 86519 |       0 | project                      
 938787 |     0 |       2 |    5 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 | 85937 |       0 | project                      
 938787 |     1 |       2 |    6 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 |   576 |   34916 | aggr   tbl=448               
 938787 |     0 |       2 |    6 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 |   576 |   34916 | aggr   tbl=448               
 938787 |     1 |       2 |    7 | 2020-05-22 11:11:48.839297 | 2020-05-22 11:11:48.865857 |        26560 |   576 |   17728 | dist                         
 938787 |     0 |       2 |    7 | 2020-05-22 11:11:48.838371 | 2020-05-22 11:11:48.865857 |        27486 |   576 |   17768 | dist                         
(16 rows)   

이 예제 출력에서는 세그먼트 값이 2일 때 쿼리가 실행되고 순차 스캔 연산을 수행하여 sales 테이블을 스캔합니다. 동일한 세그먼트에서 결과를 집계하기 위해 집계 연산이 수행되고 해시 조인 연산이 테이블을 조인하기 위해 수행됩니다. 테이블 중 하나에 대한 조인 열은 배포 키나 정렬 키가 아닙니다. 결과적으로 내부 테이블은 EXPLAIN 계획을 볼 수 있는 DS_BCAST_INNER로서 모든 컴퓨팅 노드에 배포됩니다. 

5.    세그먼트 값이 3인 쿼리에 대해 SVL_QUERY_REPORT를 가져오려면 다음 쿼리를 실행합니다.

select query,slice,segment,step,start_time,end_time,elapsed_time,rows,bytes,label from svl_query_report where query = 938787 and segment = 3 order by segment, step, elapsed_time, rows;

->  XN Sort  (cost=1002815368414.24..1002815368415.67 rows=571 width=27)
               Sort Key: sum(sales.pricepaid)
   ->  XN HashAggregate  (cost=2815368386.67..2815368388.10 rows=571 width=27)

예제 출력은 다음과 같습니다.

 query  | slice | segment | step |         start_time         |          end_time          | elapsed_time | rows | bytes |                   label                   
--------+-------+---------+------+----------------------------+----------------------------+--------------+------+-------+-------------------------------------------
 938787 |     1 |       3 |    0 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |  546 | 28792 | scan   tbl=376298 name=Internal Worktable 
 938787 |     0 |       3 |    0 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |  606 | 31824 | scan   tbl=376298 name=Internal Worktable 
 938787 |     1 |       3 |    1 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |  273 | 16580 | aggr   tbl=451                            
 938787 |     0 |       3 |    1 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |  303 | 18336 | aggr   tbl=451                            
 938787 |     1 |       3 |    2 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |  273 |     0 | project                                   
 938787 |     0 |       3 |    2 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |  303 |     0 | project                                   
 938787 |     1 |       3 |    3 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |  273 | 14396 | sort   tbl=453                            
 938787 |     0 |       3 |    3 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |  303 | 15912 | sort   tbl=453                            
 938787 |     1 |       3 |    4 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |    0 |     0 | merge                                     
 938787 |     0 |       3 |    4 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |    0 |     0 | merge                                     
 938787 |     1 |       3 |    5 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |    0 |     0 | aggr   tbl=456                            
 938787 |     0 |       3 |    5 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |    0 |     0 | aggr   tbl=456                            
 938787 |     1 |       3 |    6 | 2020-05-22 11:11:48.836693 | 2020-05-22 11:11:48.866529 |        29836 |    0 |     0 | project                                   
 938787 |     0 |       3 |    6 | 2020-05-22 11:11:48.836654 | 2020-05-22 11:11:48.866529 |        29875 |    0 |     0 | project                                   
(14 rows)

세그먼트 값이 3이고 해시 집계 연산 및 정렬 연산이 수행될 때까지 쿼리가 계속 실행됩니다. 해시 집계 연산은 정렬되지 않은 그룹화된 집계 함수에서 수행됩니다. 정렬 연산은 ORDER BY 절을 평가하기 위해 수행됩니다.

6.    세그먼트 값이 4와 5인 쿼리에 대해 SVL_QUERY_REPORT를 가져오려면 다음 쿼리를 실행합니다. 

select query,slice,segment,step,start_time,end_time,elapsed_time,rows,bytes,label from svl_query_report where query = 938787 and segment = 4  order by segment, step, elapsed_time, rows;

 query  | slice | segment | step |         start_time         |          end_time          | elapsed_time | rows | bytes |                 label                 
--------+-------+---------+------+----------------------------+----------------------------+--------------+------+-------+----------------------------------------
 938787 |     1 |       4 |    0 | 2020-05-22 11:11:48.915148 | 2020-05-22 11:11:48.915215 |           67 |  273 | 14396 | scan   tbl=453 name=Internal Worktable
 938787 |     0 |       4 |    0 | 2020-05-22 11:11:48.915148 | 2020-05-22 11:11:48.915424 |          276 |  303 | 15912 | scan   tbl=453 name=Internal Worktable
 938787 |     1 |       4 |    1 | 2020-05-22 11:11:48.915148 | 2020-05-22 11:11:48.915215 |           67 |  273 |  8888 | return                                 
 938787 |     0 |       4 |    1 | 2020-05-22 11:11:48.915148 | 2020-05-22 11:11:48.915424 |          276 |  303 |  8864 | return                                 
 938787 |  6411 |       5 |    1 | 2020-05-22 11:11:48.914501 | 2020-05-22 11:11:48.916049 |         1548 |  576 |     0 | project
 938787 |  6411 |       5 |    2 | 2020-05-22 11:11:48.914501 | 2020-05-22 11:11:48.916049 |         1548 |  576 | 18360 | return
 938787 |  6411 |       5 |    0 | 2020-05-22 11:11:48.914501 | 2020-05-22 11:11:48.916049 |         1548 |    0 |     0 | merge
 (7 rows)

모든 세그먼트를 사용한 후 쿼리는 세그먼트 4와 5에서 네트워크 연산을 실행하여 중간 결과를 리더 노드로 보냅니다. 결과는 추가 처리를 위해 리더 노드로 전송됩니다.

쿼리를 실행한 후 밀리초 단위로 쿼리의 실행 시간을 확인하려면 다음 쿼리를 사용합니다.

select datediff (ms, exec_start_time, exec_end_time) from stl_wlm_query where query= 938787;

date_diff 
-----------
 101
(1 row)

쿼리 최적화

쿼리 계획을 분석하는 동안 쿼리를 최적화하려면 다음 단계를 수행합니다.

1.    비용이 가장 높은 단계를 식별합니다.

2.    고비용 정렬 연산이 있는지 확인합니다. 쿼리의 성능은 쿼리에서 스캔하고 있는 데이터와 함께 데이터 배포 방법에 따라 달라집니다. 재배포 단계의 영향을 최소화하려면 테이블에 적합한 배포 스타일을 선택해야 합니다. 또한 적절한 열에 정렬 키를 사용하여 쿼리 속도를 높이고 스캔해야 하는 블록 수를 줄입니다. 배포 및 정렬 키를 선택하는 방법에 대한 자세한 내용은 Amazon Redshift Engineering의 고급 테이블 디자인 플레이북: 배포 스타일 및 배포 키를 참조하십시오.

다음 예에서는 STL_ALERT_EVENT_LOG 테이블을 사용하여 잠재적 쿼리 성능 문제를 식별하고 수정합니다.

select query, btrim(event) as event, btrim(solution) as solution from stl_alert_event_log where query = 940313;

 query  |              event               |          solution          
--------+----------------------------------+-------------------------
 940313 | Missing query planner statistics | Run the ANALYZE command
 (1 row)

이 예제 출력에서는 쿼리에 대한 통계가 오래되었으므로 ANALYZE 명령에 대한 쿼리를 사용하여 쿼리 성능을 향상시킬 수 있습니다.

EXPLAIN 계획을 사용하여 쿼리에 대해 채워지는 경고가 있는지 확인할 수도 있습니다.

explain select eventname, sum (pricepaid) from sales, event where sales.eventid = event.eventid group by eventname order by 2 desc;
                                                QUERY PLAN                   
                                     
-----------------------------------------------------------------------------------------------------------
 XN Merge  (cost=1029210993681.72..1029210993682.22 rows=200 width=330)
   Merge Key: sum(sales.pricepaid)
   ->  XN Network  (cost=1029210993681.72..1029210993682.22 rows=200 width=330)
         Send to leader
         ->  XN Sort  (cost=1029210993681.72..1029210993682.22 rows=200 width=330)
               Sort Key: sum(sales.pricepaid)
               ->  XN HashAggregate  (cost=29210993673.57..29210993674.07 rows=200 width=330)
                     ->  XN Hash Join DS_BCAST_INNER  (cost=109.98..29210955741.88 rows=7586340 width=330)
                           Hash Cond: ("outer".eventid = "inner".eventid)
                           ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=16)
                           ->  XN Hash  (cost=87.98..87.98 rows=8798 width=322)
                                 ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=322)
 ----- Tables missing statistics: event, sales -----
 ----- Update statistics by running the ANALYZE command on these tables -----

3.    조인 유형을 확인합니다.

참고: 중첩 루프는 주로 크로스 조인 및 일부 비균등 조인에 사용되기 때문에 가장 최적화되지 않은 조인입니다.

다음 예제에서는 두 테이블 간의 교차 조인을 보여 줍니다. 중첩된 루프 조인이 사용 중이고 첫 번째 비용 값은 0.00입니다. 이 비용 값은 교차 조인 연산의 첫 번째 행을 반환하는 관련 비용입니다. 두 번째 값 (3901467082.32)은 교차 조인 연산을 완료하는 데 드는 관련 비용을 제공합니다. 첫 번째 행과 마지막 행 간의 비용 차이를 확인합니다. 중첩 루프는 장기 실행 쿼리로 대기열을 오버로드하여 클러스터 성능에 부정적인 영향을 미칩니다.

explain select * from sales cross join event;
                                             QUERY PLAN                   
                                    
---------------------------------------------------------------------------------------------------------
 XN Nested Loop DS_BCAST_INNER  (cost=0.00..3901467082.32 rows=1517267888 width=88)
   ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=53)
   ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=35)
 ----- Nested Loop Join in the query plan - review the join predicates to avoid Cartesian products -----
(4 rows)

참고: Amazon Redshift는 테이블의 배포 스타일과 필요한 데이터 위치에 따라 조인 연산자를 선택합니다.

쿼리 성능을 최적화하기 위해 정렬 키와 배포 키가 두 테이블에 대해 “eventid”로 변경되었습니다. 다음 예에서는 해시 조인 대신 병합 조인이 사용되고 있습니다.

explain select eventname, sum (pricepaid) from sales, event where sales.eventid = event.eventid group by eventname order by 2 desc;
                                           QUERY PLAN                       
                           
------------------------------------------------------------------------------------------------
 XN Merge  (cost=1000000004965.12..1000000004966.55 rows=571 width=27)
   Merge Key: sum(sales.pricepaid)
   ->  XN Network  (cost=1000000004965.12..1000000004966.55 rows=571 width=27)
         Send to leader
         ->  XN Sort  (cost=1000000004965.12..1000000004966.55 rows=571 width=27)
               Sort Key: sum(sales.pricepaid)
               ->  XN HashAggregate  (cost=4937.55..4938.98 rows=571 width=27)
                     ->  XN Merge Join DS_DIST_NONE  (cost=0.00..4046.93 rows=178125 width=27)
                           Merge Cond: ("outer".eventid = "inner".eventid)
                           ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=14)
                           ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=21)
(11 rows)

4.    고비용 연산이 있는 브로드캐스트 연산자를 식별합니다 .

참고: 작은 테이블의 경우 작은 테이블의 재배포가 쿼리 성능에 상대적으로 영향을 미치지 않으므로 브로드캐스트 연산자가 항상 최적이 아닌 것으로 간주되는 것은 아닙니다.

5.    쿼리의 실행 시간을 확인하려면 다음 쿼리를 실행합니다.

select datediff (ms, exec_start_time, exec_end_time) from stl_wlm_query where query= 940715;

 date_diff 
-----------
 34
 (1 row)

두 쿼리의 실행 시간의 차이는 쿼리 계획이 쿼리 보고서와 올바르게 관련되어 있는지 확인합니다.


이 문서가 도움이 되었나요?


결제 또는 기술 지원이 필요합니까?