array_agg ()가 집계되지 않은 ARRAY () 생성자보다 느린 이유는 무엇입니까?


13

방금 8.5.4 이전 PostgreSQL 용으로 작성된 오래된 코드를 검토하고 있었고 정말 멋진 것을 보았습니다. 나는 커스텀 함수가 하루 중 일부를 다시하는 것을 기억하지만, 미리 array_agg()보이는 것을 잊어 버렸습니다 . 검토를 위해 현대 집계는 다음과 같이 작성됩니다.

SELECT array_agg(x ORDER BY x DESC) FROM foobar;

그러나 옛날 옛적에 이렇게 쓰여졌습니다

SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);

그래서 테스트 데이터로 시도했습니다.

CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
  AS t(x);

그 결과는 놀라웠습니다. #OldSchoolCool 방식은 엄청나게 빨랐습니다 : 25 % 속도 향상. 또한 ORDER 없이 단순화 하면 동일한 속도가 느려졌습니다.

# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
 Planning time: 0.068 ms
 Execution time: 1671.482 ms
(5 rows)

test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
   ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
 Planning time: 0.054 ms
 Execution time: 2174.753 ms
(4 rows)

그래서 여기서 무슨 일이 일어나고 있습니까? 내부 함수 array_agg 가 플래너의 SQL 부두보다 훨씬 느린 이유는 무엇 입니까?

" gcc에 의해 컴파일 된 x86_64-pc-linux-gnu에서 PostgreSQL 9.5.5 사용 (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64 비트"

답변:


17

ARRAY 생성자 에 대한 "오래된 학교"나 "오래된"것은 없습니다 (이것이 무엇입니까 ARRAY(SELECT x FROM foobar)). 그 어느 때보 다 현대적입니다. 간단한 배열 집계에 사용하십시오.

매뉴얼 :

하위 쿼리 결과에서 배열을 구성 할 수도 있습니다. 이 형식에서, 배열 생성자는 키워드 ARRAY뒤에 괄호로 묶인 (괄호로 묶이지 않은) 서브 쿼리로 작성됩니다.

집계 기능은array_agg() 훨씬 더가 통합 될 수 있다는 점에서 다재다능 SELECT같은 더 많은 열이 더 가능 집계하여리스트 SELECT및 임의의 그룹을 형성 할 수있다 GROUP BY. 하면서 An ARRAY 생성자 만으로부터 단일 배열을 반환 할 수 SELECT단일 열을 반환.

소스 코드를 연구하지는 않았지만 훨씬 더 다양한 도구가 더 비싸다는 것이 분명해 보입니다.


array_aggARRAY생성자가 UNION내부적으로 표현식과 거의 비슷한 일을하는 것처럼 보이는 입력 순서를 추적해야합니다 . 추측을 array_agg해야한다면 더 많은 메모리가 필요할 것입니다. 철저하게 테스트 할 수는 없지만 우분투 16.04에서 실행되는 PostgreSQL 9.6에서는 ARRAY()쿼리가 ORDER BY외부 병합 을 사용했으며 array_agg쿼리 보다 느 렸습니다 . 당신이 말했듯이, 코드를 읽지 못하면 대답은 우리가 가장 잘 설명하는 것입니다.
Jeff

@Jeffrey : 당신은 테스트 케이스 발견 array_agg()입니다 빨리 배열 생성자 이상을? 간단한 경우? Postgres가 비용 계획의 부정확 한 통계를 기반으로 한 쿼리 계획에 대한 결정을 기반으로했기 때문에 그럴 가능성은 적습니다. array_agg()배열 생성자를 능가하는 것을 본 적이 없으며 여러 번 테스트했습니다.
Erwin Brandstetter

1
@ Jeffrey : 잘못된 캐싱 효과가 없습니까? 각 쿼리를 두 번 이상 실행 했습니까? 더 많은 것을 말하려면 테이블 정의, 카디널리티 및 정확한 쿼리를 볼 필요가 있습니다.
Erwin Brandstetter

1
이것은 실제 답변이 아닙니다. 보다 다양한 도구뿐만 아니라 더 다양한 도구를 사용할 수 있습니다. 다재다능한 것이 실제로 속도를 늦추는 이유는 무엇입니까?
Gavin Wahl

1
@Jeffrey : Postgres가 각 변형에 대해 서로 다른 정렬 알고리즘을 선택하는 것 같습니다 (비용 추정 및 테이블 통계에 따라). 그리고 ARRAY 생성자에 대해 열등한 방법을 선택하게됩니다. 이는 예상 비용 계산에서 하나 이상의 요소가 너무 멀다는 것을 나타냅니다. 이것은 임시 테이블에 있습니까? VACUUM ANALYZE쿼리를 실행하기 전에 했습니까 ? 고려 : dba.stackexchange.com/a/18694/3684
Erwin Brandstetter

5

Erwin이 수락 한 답변은 다음과 같이 추가 될 수 있다고 생각합니다.

일반적으로 우리는 원래 질문에서와 같이 임시 테이블 (인덱스 제외) 대신 인덱스가있는 일반 테이블로 작업합니다. 와 같은 ARRAY_AGG집계는 집계 중에 정렬이 수행 될 때 기존 인덱스를 활용할 수 없다는 점에 유의하는 것이 좋습니다.

예를 들어, 다음 쿼리를 가정하십시오.

SELECT ARRAY(SELECT c FROM t ORDER BY id)

인덱스가 on t(id, ...)이면 순차적 스캔 on t및 정렬 on 을 선호하여 인덱스를 사용할 수 있습니다 t.id. 또한 배열 (여기서 c) 에 랩핑되는 출력 열이 인덱스의 일부인 경우 (예 : 인덱스 t(id, c)또는 인덱스 포함) 인덱스 t(id) include(c)전용 스캔 일 수도 있습니다.

이제 다음과 같이 해당 쿼리를 다시 작성하겠습니다.

SELECT ARRAY_AGG(c ORDER BY id) FROM t

이제 집계는 인덱스를 사용하지 않으며 메모리에서 행을 정렬해야합니다 (또는 디스크의 큰 데이터 세트의 경우 더 나빠짐). 이것은 항상 순차적 스캔이며 t그 다음에 aggregation + sort가 됩니다.

내가 아는 한, 이것은 공식 문서에 문서화되어 있지 않지만 소스에서 파생 될 수 있습니다. v11이 포함 된 모든 현재 버전의 경우에 해당합니다.


2
좋은 지적. 그러나 모든 공정성에서 array_agg()집계 함수 가 있거나 유사한 쿼리 는 여전히 다음과 같은 하위 쿼리로 인덱스를 활용할 수 있습니다 SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub. 집계 별 ORDER BY절은 예제에서 인덱스 사용을 금지합니다. 배열 생성자는 같은 인덱스를 사용할 수있는 보다 빠릅니다array_agg() . 다재다능하지 않습니다. 참조 : dba.stackexchange.com/a/213724/3684
어윈 Brandstetter

1
맞습니다, 그것은 중요한 구별입니다. 집계 함수를 정렬해야 할 때만이 설명이 유지되도록하기 위해 대답을 약간 변경했습니다. PostgreSQL은 링크에서 설명 된 것처럼 하위 쿼리에 정의 된 것과 동일한 순서로 집계가 발생한다는 것을 보장하기 때문에 간단한 경우에도 여전히 인덱스에서 이익을 얻을 수 있습니다. 꽤 멋지다. 분할 된 테이블 및 / 또는 FDW 테이블 및 / 또는 병렬 작업자의 경우에도 여전히 이것이 있는지, PostgreSQL이 향후 릴리스 에서이 약속을 유지할 수 있는지 궁금합니다.
pbillen

기록을 위해, 나는 결코 받아 들여진 대답을 의심 할 의사가 없었다. 나는 그것이 집계와 함께 인덱스의 존재와 사용에 대한 이유에 대한 좋은 추가라고 생각했습니다.
pbillen

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