답변:
Explaining_EXPLAIN.pdf 도 도움이 될 수 있습니다.
내가 항상 헷갈리는 부분은 시작 비용과 총 비용입니다. 나는 그것을 잊을 때마다 이것을 구글로 여기고 여기로 돌아와서 차이점을 설명하지 못합니다. 이것이 제가이 답변을 쓰는 이유입니다. 이 내가에서 수집 한 것입니다 포스트 그레스 EXPLAIN
문서 , 내가 그것을 이해 설명했다.
다음은 포럼을 관리하는 애플리케이션의 예입니다.
EXPLAIN SELECT * FROM post LIMIT 50;
Limit (cost=0.00..3.39 rows=50 width=422)
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
다음은 PgAdmin의 그래픽 설명입니다.
(PgAdmin을 사용하는 경우 구성 요소를 마우스로 가리키면 비용 세부 정보를 읽을 수 있습니다.)
비용은 튜플로 표시됩니다. 예를 들어 LIMIT
is cost=0.00..3.39
비용과 순차적 스캔 비용 post
은 cost=0.00..15629.12
. 튜플의 첫 번째 숫자는 시작 비용 이고 두 번째 숫자는 총 비용 입니다. 내가 사용 EXPLAIN
하고 사용 하지 않았기 때문에 EXPLAIN ANALYZE
이러한 비용은 실제 측정이 아닌 추정치입니다.
문제로 각 "상위"노드의 비용에는 하위 노드의 비용이 포함됩니다. 텍스트 표현에서 트리는 들여 쓰기로 표시됩니다. 예를 들어 LIMIT
부모 노드이고 Seq Scan
그 자식입니다. PgAdmin 표현에서 화살표는 자식에서 부모 (데이터 흐름의 방향)를 가리 킵니다. 그래프 이론에 익숙하다면 직관적이지 않을 수 있습니다.
문서에 따르면 비용에는 모든 하위 노드가 포함되지만 상위 3.39
의 총 비용은 하위의 총 비용보다 훨씬 적습니다 15629.12
. 같은 구성 요소 LIMIT
는 전체 입력을 처리 할 필요가 없기 때문에 총 비용은 포함 되지 않습니다. Postgres 설명서 의 EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
예제를 참조하십시오 .EXPLAIN
위의 예에서 시작 시간은 두 구성 요소 모두에 대해 0입니다. 두 구성 요소 모두 행 쓰기를 시작하기 전에 처리를 수행 할 필요가 없기 때문입니다. 순차 스캔은 테이블의 첫 번째 행을 읽고이를 내 보냅니다. 는 LIMIT
자사의 첫 번째 행을 읽고 다음을 방출한다.
구성 요소가 행 출력을 시작하기 전에 많은 처리를 수행해야하는시기는 언제입니까? 가능한 많은 이유가 있지만 한 가지 명확한 예를 살펴 보겠습니다. 다음은 이전과 동일한 쿼리이지만 이제 ORDER BY
절을 포함합니다 .
EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;
Limit (cost=23283.24..23283.37 rows=50 width=422)
-> Sort (cost=23283.24..23859.27 rows=230412 width=422)
Sort Key: body
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
그리고 그래픽 :
다시 한 번, 순차 스캔 post
에는 시작 비용이 없습니다. 즉, 행 출력을 즉시 시작합니다. 그러나 정렬은 단일 행을 출력하기 전에 전체 테이블23283.24
을 정렬해야 하므로 시작 비용이 많이 듭니다 . 정렬의 총 비용은 23859.27
시작 비용보다 약간 높으며, 전체 데이터 세트가 정렬되면 정렬 된 데이터를 매우 빠르게 내보낼 수 있다는 사실을 반영합니다.
공지의 시작 시간은 LIMIT
23283.24
정렬의 시작 시간을 정확히 동일합니다. 이는 LIMIT
자체적으로 시작 시간이 길기 때문이 아닙니다 . 실제로 자체적으로 시작 시간은 0이지만 EXPLAIN
각 부모의 모든 자식 비용을 롤업하므로 LIMIT
시작 시간에는 자식의 총 시작 시간이 포함됩니다.
이러한 비용 롤업은 각 개별 구성 요소의 실행 비용을 이해하기 어렵게 만들 수 있습니다. 예를 들어, 우리 LIMIT
는 시작 시간이 0이지만 언뜻보기에는 분명하지 않습니다. 이러한 이유로, 여러 가지 다른 사람들과 연결 explain.depesz.com , 이해하는 데 도움이 휴 버트 Lubaczewski (일명 depesz)에 의해 생성 된 도구 EXPLAIN
무엇보다도 - - 부모의 비용에서 아이의 비용을 빼서을. 그는 자신의 도구에 대한 짧은 블로그 게시물 에서 몇 가지 다른 복잡성을 언급합니다 .
가장 들여 쓴 것부터 가장 적게 들여 쓴 것까지 실행되며 계획의 맨 아래에서 맨 위로 이동한다고 생각합니다. (두 개의 들여 쓰기 된 섹션이있는 경우 페이지 아래쪽에있는 섹션이 먼저 실행되고 다른 섹션을 만나면 해당 섹션을 결합하는 규칙이 실행됩니다.)
아이디어는 각 단계에서 도착하여 규칙에 따라 처리되는 1 개 또는 2 개의 데이터 세트가 있다는 것입니다. 데이터 세트가 하나뿐이면 해당 작업이 해당 데이터 세트에 수행됩니다. (예를 들어 색인을 스캔하여 원하는 행을 파악하거나 데이터 세트를 필터링하거나 정렬합니다.) 두 데이터 세트가 두 개이면 더 들여 쓰기되는 두 가지가되고 표시되는 규칙에 따라 결합됩니다. 대부분의 규칙의 의미는 합리적으로 쉽게 추측 할 수 있지만 (특히 이전에 많은 설명 계획을 읽은 경우) 문서를 살펴 보거나 문구를 삽입하여 (쉽게) 개별 항목을 확인할 수 있습니다. Google과 EXPLAIN
.
이것은 분명히 완전한 설명은 아니지만 일반적으로 원하는 것을 파악할 수있는 충분한 컨텍스트를 제공합니다. 예를 들어 실제 데이터베이스에서이 계획을 고려하십시오.
explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';
------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge Join (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
Merge Cond: (a.orderid = b.orderid)
-> Sort (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
Sort Key: a.orderid
Sort Method: quicksort Memory: 1695kB
-> Bitmap Heap Scan on orderitemattribute a (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
Recheck Cond: ((attributeid)::text = 'display-album'::text)
-> Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
Index Cond: ((attributeid)::text = 'display-album'::text)
-> Sort (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
Sort Key: b.orderid
Sort Method: quicksort Memory: 76kB
-> Bitmap Heap Scan on orderitem b (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
Recheck Cond: ((productid)::text = 'ModernBook'::text)
-> Bitmap Index Scan on id_orderitem_productid (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
Index Cond: ((productid)::text = 'ModernBook'::text)
Total runtime: 842.134 ms
(17 rows)
직접 읽어보고 이해가되는지 확인하십시오.
내가 읽은 것은 데이터베이스가 먼저 id_orderitem_productid
인덱스를 사용하여 원하는 행을 찾은 orderitem
다음 빠른 정렬을 사용하여 해당 데이터 집합을 정렬 한 다음 (데이터가 RAM에 맞지 않으면 사용되는 정렬이 변경됨)이를 따로 설정한다는 것입니다.
다음으로 스캔 orditematt_attributeid_idx
하여 원하는 행을 찾은 orderitemattribute
다음 빠른 정렬을 사용하여 해당 데이터 세트를 정렬합니다.
그런 다음 두 데이터 세트를 가져와 병합합니다. (병합 조인은 정렬 된 두 데이터 세트를 병렬로 이동하여 일치 할 때 조인 된 행을 내보내는 일종의 "압축"작업입니다.)
내가 말했듯이, 당신은 계획 내부 부분에서 외부 부분까지, 아래에서 위로 작업합니다.
PostgreSQL의 공식 문서 는 Explain의 출력을 이해하는 방법에 대한 흥미롭고 철저한 설명을 제공합니다.