쿼리 튜닝을 위한 진단 쿼리 - Amazon Redshift

쿼리 튜닝을 위한 진단 쿼리

아래 쿼리들은 쿼리 성능에 영향을 미칠 수 있는 쿼리 또는 쿼리 테이블의 문제를 식별하는 데 사용됩니다. 이러한 쿼리는 쿼리 분석 및 개선에서 언급한 쿼리 튜닝 프로세스와 함께 사용하는 것이 좋습니다.

튜닝에 가장 적합한 쿼리 식별

다음은 지난 7일 동안 실행한 쿼리 문 중에서 가장 많은 시간이 소요된 문 50개를 구분하는 쿼리입니다. 결과를 사용하면 비정상적으로 오래 걸리는 쿼리를 식별할 수 있습니다. 또한 자주 실행되는 쿼리(결과 집합에 두 번 이상 나타나는 쿼리)를 식별할 수 있습니다. 이러한 쿼리들은 튜닝을 통해 시스템 성능을 개선하기 좋은 후보들입니다.

이 쿼리는 식별된 각 쿼리와 연결되어 있는 알림 이벤트의 수도 반환합니다. 이러한 알림 이벤트를 통해 쿼리 성능을 개선하는 데 필요한 세부 정보를 알아낼 수 있습니다. 자세한 내용은 쿼리 알림 검토 섹션을 참조하세요.

select trim(database) as db, count(query) as n_qry, max(substring (qrytext,1,80)) as qrytext, min(run_minutes) as "min" , max(run_minutes) as "max", avg(run_minutes) as "avg", sum(run_minutes) as total, max(query) as max_query_id, max(starttime)::date as last_run, sum(alerts) as alerts, aborted from (select userid, label, stl_query.query, trim(database) as database, trim(querytxt) as qrytext, md5(trim(querytxt)) as qry_md5, starttime, endtime, (datediff(seconds, starttime,endtime)::numeric(12,2))/60 as run_minutes, alrt.num_events as alerts, aborted from stl_query left outer join (select query, 1 as num_events from stl_alert_event_log group by query ) as alrt on alrt.query = stl_query.query where userid <> 1 and starttime >= dateadd(day, -7, current_date)) group by database, label, qry_md5, aborted order by total desc limit 50;

데이터 스큐 또는 미정렬 행이 포함된 테이블 식별

다음은 데이터 분산이 균일하지 못하거나(데이터 스큐), 정렬되지 않은 행의 비율이 높은 테이블을 찾아내는 쿼리입니다.

skew 값이 낮으면 테이블 데이터가 올바로 분산된 것을 의미합니다. 테이블의 skew 값이 4.00 이상이면 데이터 분산 스타일을 수정하는 것이 좋습니다. 자세한 내용은 최적이 아닌 데이터 분산 섹션을 참조하세요.

테이블의 pct_unsorted 값이 20%보다 높으면 VACUUM 명령을 실행하는 것이 좋습니다. 자세한 내용은 정렬되지 않았거나 잘못 정렬된 행 섹션을 참조하세요.

그 밖에도 각 테이블마다 mbytes 값과 pct_of_total 값을 살펴봐야 합니다. 이러한 열은 테이블 크기를 비롯해 원시 디스크에서 테이블이 사용하는 공간 비율을 나타냅니다. 원시 디스크 공간에는 Amazon Redshift가 내부 사용 목적으로 예약하는 공간도 포함되므로 사용자가 사용할 수 있는 디스크 공간 크기인 공칭 디스크 용량보다 더 커야 합니다. 이 정보를 사용하여 여유 디스크 공간이 가장 큰 테이블 크기의 2.5배 이상인지 확인합니다. 이 정도 크기의 공간을 사용할 수 있도록 유지하면 시스템이 복합 쿼리를 처리할 때도 중간 결과를 디스크에 작성할 수 있습니다.

select trim(pgn.nspname) as schema, trim(a.name) as table, id as tableid, decode(pgc.reldiststyle,0, 'even',1,det.distkey ,8,'all') as distkey, dist_ratio.ratio::decimal(10,4) as skew, det.head_sort as "sortkey", det.n_sortkeys as "#sks", b.mbytes, decode(b.mbytes,0,0,((b.mbytes/part.total::decimal)*100)::decimal(5,2)) as pct_of_total, decode(det.max_enc,0,'n','y') as enc, a.rows, decode( det.n_sortkeys, 0, null, a.unsorted_rows ) as unsorted_rows , decode( det.n_sortkeys, 0, null, decode( a.rows,0,0, (a.unsorted_rows::decimal(32)/a.rows)*100) )::decimal(5,2) as pct_unsorted from (select db_id, id, name, sum(rows) as rows, sum(rows)-sum(sorted_rows) as unsorted_rows from stv_tbl_perm a group by db_id, id, name) as a join pg_class as pgc on pgc.oid = a.id join pg_namespace as pgn on pgn.oid = pgc.relnamespace left outer join (select tbl, count(*) as mbytes from stv_blocklist group by tbl) b on a.id=b.tbl inner join (select attrelid, min(case attisdistkey when 't' then attname else null end) as "distkey", min(case attsortkeyord when 1 then attname else null end ) as head_sort , max(attsortkeyord) as n_sortkeys, max(attencodingtype) as max_enc from pg_attribute group by 1) as det on det.attrelid = a.id inner join ( select tbl, max(mbytes)::decimal(32)/min(mbytes) as ratio from (select tbl, trim(name) as name, slice, count(*) as mbytes from svv_diskusage group by tbl, name, slice ) group by tbl, name ) as dist_ratio on a.id = dist_ratio.tbl join ( select sum(capacity) as total from stv_partitions where part_begin=0 ) as part on 1=1 where mbytes is not null order by mbytes desc;

중첩 루프가 포함된 쿼리 식별

다음은 중첩 루프에 대한 알림 이벤트가 기록된 쿼리를 식별하는 쿼리입니다. 중첩 루프 조건을 해결하는 방법에 대한 자세한 내용은 중첩 루프 섹션을 참조하세요.

select query, trim(querytxt) as SQL, starttime from stl_query where query in ( select distinct query from stl_alert_event_log where event like 'Nested Loop Join in the query plan%') order by starttime desc;

쿼리의 대기열 대기 시간 검토

다음은 최근 쿼리가 실행에 앞서 쿼리 대기열의 슬롯이 열릴 때까지 대기한 시간을 나타내는 쿼리입니다. 대기 시간이 높은 추이가 발견되면 쿼리 대기열 구성을 수정하여 처리량을 개선하는 것이 좋습니다. 자세한 내용은 수동 WLM 구현 섹션을 참조하세요.

select trim(database) as DB , w.query, substring(q.querytxt, 1, 100) as querytxt, w.queue_start_time, w.service_class as class, w.slot_count as slots, w.total_queue_time/1000000 as queue_seconds, w.total_exec_time/1000000 exec_seconds, (w.total_queue_time+w.total_Exec_time)/1000000 as total_seconds from stl_wlm_query w left join stl_query q on q.query = w.query and q.userid = w.userid where w.queue_start_Time >= dateadd(day, -7, current_Date) and w.total_queue_Time > 0 and w.userid >1 and q.starttime >= dateadd(day, -7, current_Date) order by w.total_queue_time desc, w.queue_start_time desc limit 35;

테이블별 쿼리 알림 검토

다음은 테이블 자체에 대한 알림 이벤트가 기록된 테이블을 찾아내는 동시에 가장 자주 기록되는 알림 유형을 식별하는 쿼리입니다.

식별된 테이블에서 행의 minutes 값이 높으면 테이블에서 해당 테이블에 대한 ANALYZE 또는 VACUUM 실행과 같은 일상적인 유지 관리가 필요한지를 확인합니다.

임의의 행에서 count 값이 높을 때 table 값이 NULL이라면 STL_ALERT_EVENT_LOG에서 연결된 event 값에 대한 쿼리를 실행하여 알림이 잦은 이유를 조사하세요.

select trim(s.perm_table_name) as table, (sum(abs(datediff(seconds, s.starttime, s.endtime)))/60)::numeric(24,0) as minutes, trim(split_part(l.event,':',1)) as event, trim(l.solution) as solution, max(l.query) as sample_query, count(*) from stl_alert_event_log as l left join stl_scan as s on s.query = l.query and s.slice = l.slice and s.segment = l.segment and s.step = l.step where l.event_time >= dateadd(day, -7, current_Date) group by 1,3,4 order by 2 desc,6 desc;

통계가 누락된 테이블 식별

다음은 통계가 누락된 테이블에 대한 쿼리 수를 제공하는 쿼리입니다. 이 쿼리가 어떤 행이든 반환하는 경우에는 plannode 값을 살펴보면서 해당 테이블을 확인한 후 ANALYZE를 실행하세요.

select substring(trim(plannode),1,100) as plannode, count(*) from stl_explain where plannode like '%missing statistics%' group by plannode order by 2 desc;