2017-12-14 21 views
1

~ 150M 레코드 테이블에서 고유 한 숫자 ID로 식별되는 1,000 - 50,000 개의 레코드를 검색해야합니다. 우리는 AWS RDS에서 데이터베이스를 호스팅하고 있습니다. 이 테이블에는 여러 개의 integer 열이 있는데 하나는 character varying(500)이고 id 열은 bigint입니다. 모든 열은 btree 색인을가집니다.Postgres : IN 대 JOIN을 사용하여 많은 수의 행 선택

현재 생산 쿼리이 허용 m < 1,000 1 초 이내에 반환

SELECT * 
FROM mytable t 
WHERE id IN (N1, N2, .. Nm) 

이다. 문제는 m이 선형 적으로 증가하는 것입니다. 쿼리는 m = 30,000에 대해 20 초 이상 걸립니다.

인덱스 된 임시 테이블을 만들고 INNER JOIN을 사용하여 성능이 크게 개선되지 않았습니다. (https://stackoverflow.com/a/24647700/226960)

다음은 m> 70k에 대한 축약 된 덤프입니다.

CREATE TEMPORARY TABLE temp_phrases (phrase_id integer) ON COMMIT DROP 
CREATE TABLE temp_phrases: OK, time: 0.01 seconds. 
CREATE INDEX temp_phrases_phrase_id_idx ON temp_phrases(phrase_id) 
CREATE INDEX '.temp_phrases.'_phrase_id_idx: OK, time: 0 seconds. 
INSERT INTO TABLE temp_phrases: 70544, time: 0.3 seconds. 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
EXPLAIN SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
Nested Loop Left Join (cost=0.57..665368.61 rows=79815 width=34) 
-> Seq Scan on temp_phrases (cost=0.00..1111.15 rows=79815 width=4) 
-> Index Scan using thesaurus_pkey on thesaurus (cost=0.57..8.31 rows=1 width=42) 
    Index Cond: (id = temp_phrases.phrase_id) 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
TEMP TABLE AND JOIN: 70544 results, 52.2seconds 

이있는 하드웨어 관련 병목
https://stackoverflow.com/a/24254825/226960

원래 id IN(_list_) 쿼리를 향상시킬 수있다 나타냅니다, 우리는 쿼리를 반복하면 결과를 얻을 1 초 미만 소요? 추가 RDS IOPS가 도움이 될까요?

편집
EXPLAIN (ANALYZE, BUFFERS) 출력

INSERT INTO TABLE temp_phrases: 41504, time: 0.17 seconds. 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
Nested Loop Left Join (cost=0.57..396319.90 rows=46920 width=34) (actual time=0.708..23874.200 rows=41504 loops=1) 
    Buffers: shared hit=167593 read=39458 dirtied=138, local hit=184 
    -> Seq Scan on temp_phrases (cost=0.00..653.20 rows=46920 width=4) (actual time=0.012..21.138 rows=41504 loops=1) 
     Buffers: local hit=184 
    -> Index Scan using thesaurus_pkey on thesaurus (cost=0.57..8.42 rows=1 width=42) (actual time=0.569..0.572 rows=1 loops=41504) 
     Index Cond: (id = temp_phrases.phrase_id) 
     Buffers: shared hit=167593 read=39458 dirtied=138 
Planning time: 1.493 ms 
Execution time: 23887.493 ms 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
TEMP TABLE AND JOIN: 41504 results, 24.2seconds 
+0

'IN' 대신'EXISTS'를 시도하십시오. 나는 네가 더 나은 성적을 얻을 것이라고 믿는다. –

+1

** [편집] ** 귀하의 질문에 ** ** explain (analyze, buffers) **을 사용하여 생성 된 실행 계획을 추가하십시오. [** 포맷 된 텍스트 **] (http://stackoverflow.com/help/formatting)하시기 바랍니다. [스크린 샷 없음] (http://meta.stackoverflow.com/questions/285551/why-may-i-not -Upload-images-of-code-on-so-ask-a-question/285557 # 285557) –

+0

INSERT INTO temp_phrases 다음에 (VACUUM) ANALYZE를 수행하십시오. 이렇게하면 통계가 업데이트되고 색인 계획이 생성 될 수 있습니다. – joop

답변

0

모든 BTREE 인덱스, 또는 전혀 인덱스를 사용하여, 그냥 행의 모든 ​​단일 조합을 일치하려고 할 것입니다.

그러나 경기가 끝난 후 중지하게되면 속도가 두 배 빨라집니다.

임시 테이블의 고유 키 또는 기본 키. 당신이 그것에 참여할 때, 하나의 일치가 발견되면, 다른 일치는 발견되지 않을 것임을 알게 될 것입니다.

제 경험상 A에서 선택하고 B에 합류하면 고유 키에 가입하는 데 많은 도움이됩니다. 성능 향상은 보통 간단한 색인과 비교하여 50 % 적은 시간입니다.

다른 상황에서는 고유 키를 사용할 수없는 경우 해시 색인은 색인 생성에 다소 시간이 걸리지 만 동일한 작업을 수행합니다.

그러나 계획에서 볼 수있는 것처럼 임시 테이블의 인덱스 스캔이 아닌 시퀀스 스캔을 수행합니다. 어떤 인덱스를 사용하면 도움이 될 것입니다. 튜플이 꽤 많기 때문입니다.

+0

Craig Ringer는 여기 https://stackoverflow.com/a/24647700/226960을 권장하지만 임시 테이블에 인덱스가 있지만 강제로 적용하지는 않습니다. a_horse_with_no_name에 따르면 postgres (및 다른 엔진)는 5-10 %보다 많은 행이 반환되는 경우 순차를 사용합니다. https://stackoverflow.com/a/5203827/226960 – BojanG

+0

이론적 인면에서는 그다지 강하지 못하지만 3M 행 테이블 B와 함께 10M 행 테이블에 참여한 경험을 통해 알 수 있습니다. 표 B의 행이 선택되었습니다. 조인 조건에 대해 고유 한 키를 추가하면 상황이 더 빨라지고 일관되게 여러 경우에 적용됩니다. 그러나 더 조심스럽게 계획을 보면'thesaurus' 테이블이이 계획에서이 쿼리의 고유 키로부터 이익을 얻을 수있는 것처럼 보입니다. 아니면 해시 색인 일 수도 있습니다. – AlexanderMP