2013-08-29 3 views
1

I가 큰 데이터베이스에 느린하는 다음과 같은 선택 :- PostgreSQL의

SELECT eventid 
FROM track_event 
WHERE inboundid IN (SELECT messageid FROM temp_message); 

temp_message 테이블은 작은 (100 행) 및 하나의 열 (주의 messageid VARCHAR)입니다 열의 btree 인덱스

track_event 테이블에는 19 개의 열과 거의 1300 만 개의 행이 있습니다. 이 쿼리에 사용 된 열 (eventid bigint 및 inboundid varchar)에는 모두 btree 인덱스가 있습니다.

나는이 큰 데이터베이스에서 계획을 설명 복사/붙여 넣기,하지만 여기에 같은 스키마 (track_event 만 348 행) 작은 데이터베이스에서 계획의 수 :

explain analyse SELECT eventid FROM track_event WHERE inboundid IN (SELECT messageid FROM temp_message); 
                  QUERY PLAN                
---------------------------------------------------------------------------------------------------------------------------------------- 
Nested Loop Semi Join (cost=0.00..60.78 rows=348 width=8) (actual time=0.033..3.186 rows=348 loops=1) 
-> Seq Scan on track_event (cost=0.00..8.48 rows=348 width=25) (actual time=0.012..0.860 rows=348 loops=1) 
-> Index Scan using temp_message_idx on temp_message (cost=0.00..0.48 rows=7 width=32) (actual time=0.005..0.005 rows=1 loops=348) 
     Index Cond: ((temp_message.messageid)::text = (track_event.inboundid)::text) 
Total runtime: 3.349 ms 
(5 rows) 

큰 데이터베이스에 이 쿼리에는 약 450 초가 걸립니다. 누구나 분명한 속도 향상을 볼 수 있습니까? 설명 계획에 track_event의 Seq Scan이 있음을 알았습니다. 잃어 버리고 싶지만 대신 사용할 수있는 인덱스를 찾을 수는 없습니다.

포스트 그레스 9.0

track_event 테이블 내가 큰 변화를 만들 수없는 매우 큰 복잡한 스키마의 일부를 편집합니다. 방금 추가 한 새 색인을 포함한 정보는 다음과 같습니다.

  Table "public.track_event" 
     Column  |   Type   | Modifiers 
--------------------+--------------------------+----------- 
eventid   | bigint     | not null 
messageid   | character varying  | not null 
inboundid   | character varying  | not null 
newid    | character varying  | 
parenteventid  | bigint     | 
pmmuser   | bigint     | 
eventdate   | timestamp with time zone | not null 
routeid   | integer     | 
eventtypeid  | integer     | not null 
adminid   | integer     | 
hostid    | integer     | 
reason    | character varying  | 
expiry    | integer     | 
encryptionendpoint | character varying  | 
encryptionerror | character varying  | 
encryptiontype  | character varying  | 
tlsused   | integer     | 
tlsrequested  | integer     | 
encryptionportal | integer     | 
Indexes: 
    "track_event_pk" PRIMARY KEY, btree (eventid) 
    "foo" btree (inboundid, eventid) 
    "px_event_inboundid" btree (inboundid) 
    "track_event_idx" btree (messageid, eventtypeid) 
Foreign-key constraints: 
    "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid) 
    "track_event_pmi_route_fk" FOREIGN KEY (routeid) REFERENCES pmi_route(routeid) 
    "track_event_pmim_smtpaddress_fk" FOREIGN KEY (pmmuser) REFERENCES pmim_smtpaddress(smtpaddressid) 
    "track_event_track_adminuser_fk" FOREIGN KEY (adminid) REFERENCES track_adminuser(adminid) 
    "track_event_track_encryptionportal_fk" FOREIGN KEY (encryptionportal) REFERENCES track_encryptionportal(id) 
    "track_event_track_eventtype_fk" FOREIGN KEY (eventtypeid) REFERENCES track_eventtype(eventtypeid) 
    "track_event_track_host_fk" FOREIGN KEY (hostid) REFERENCES track_host(hostid) 
    "track_event_track_message_fk" FOREIGN KEY (inboundid) REFERENCES track_message(messageid) 
Referenced by: 
    TABLE "track_event" CONSTRAINT "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid) 
    TABLE "track_eventaddress" CONSTRAINT "track_eventaddress_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventattachment" CONSTRAINT "track_eventattachment_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventrule" CONSTRAINT "track_eventrule_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventthreatdescription" CONSTRAINT "track_eventthreatdescription_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventthreattype" CONSTRAINT "track_eventthreattype_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_quarantineevent" CONSTRAINT "track_quarantineevent_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
+0

1) mesaage_id가 varchar 유형이되는 이유는 무엇입니까? 2) 대용 기본 키를 추가하고 FK로 사용하고 텍스트 열에 고유 제한 조건을 추가 할 수 있습니까? 3) 'IN (...)'에 'EXISTS (...)'를 선호 할 수 있습니까? (계획에 이미 색인 스캔이 표시되어 있음에도 불구하고) 0) 테이블 정의를 질문에 추가하십시오. 0a)와 튜너 블 (random_page_cost, work_mem,?) – joop

+1

BTW : 인덱스 조건에서의 캐스팅'((temp_message.messageid) :: text = (track_event.inboundid) :: text) '은 ** 매우 용의주 함 ** 나는 그것을 여기에서 재현 할 수 없다. (pg9.3 베타 : varchar 키에 대해서만 hashjoins를 얻는다.) Postgres 버전? ** 테이블 정의 **? – joop

+0

joop이 Postgres 버전을 추가한다고 말했습니다. 계획과 버전이 없으면 답을 얻을 수 없습니다. – Kuberchaun

답변

1

큰 테이블에서 전체 테이블 검색을 수행 중입니다. 분명한 속도는 event_track(inboundid, eventid)에 색인을 추가하는 것입니다. Postgres는 쿼리의 색인을 서면으로 사용할 수 있어야합니다. 다음과 같이 쿼리를 다시 작성할 수 있습니다.

SELECT te.eventid 
FROM track_event te join 
    temp_message tm 
    on te.inboundid = tm.messageid; 

인덱스를 반드시 사용해야합니다. 합니다 (temp_message 테이블에 중복이있는 경우 select distinct te.eventid이 필요할 수 있습니다.)

편집 :

마지막으로 요청한 재 작성 쿼리 반전하는 것입니다

:

select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid 
from temp_message tm; 

이 인덱스의 사용을 강제해야한다을 . 비 일치가있는 경우, 당신은 할 수 있습니다 :

select eventid 
from (select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid 
     from temp_message tm 
    ) tm 
where eventid is not null; 
+0

track_event에 Seq Scan이 여전히 있습니다. – NickJ

+2

통계가 잘못되었거나 없을 수 있습니다. 또는 예상 행 수가 10 % (IIRC)보다 낮거나 DBMS 튜닝 상수가 잘못되었습니다. – joop

+0

쿼리 계획은 테이블의 크기에 따라 다를 수 있습니다. 큰 테이블에서 테스트해야합니다. – klin

1

이 기술하십시오 :

SELECT eventid FROM track_event te WHERE inboundid IN (SELECT messageid FROM temp_message where messageid = te.inboundid); 

을 또는 당신은 또한 더 나은 결과

Select eventid From track_event te Where (Select count(*) from temp_message where messageid = te.inboundid) > 0 
1

그것은 다름에 대해 다음 코드를 사용할 수 있습니다 데이터베이스의 레코드 수 (특정 테이블). 소규모 데이터베이스 및 데이터베이스의 본성 정적 매우 드문 기록적인 증가가있는 경우 사용이 더 나은 다음 IN 후 테이블로 작은 테이블에 어떤것 동작합니다 가입 가입하기 때문에 마이크로 초 정도 걸립니다 조인.어디서 IN이 실행 어떤것의 특정 시간을 가지고 있기 때문에 데이터베이스가 데이터베이스가 한 Statment에서 사용하는 경우 다음 작은 경우 다음 어떤것 빠른 결과를 얻을 수 큰 경우 대형 데이터베이스에 더 남아 쿼리 작은을 위해 더 많은 시간

소요 대형 데이터베이스에 대한 데이터베이스

SELECT t1.column_name,t1.column_name,t2.column_name,t2.column_name FROM tbl1 t1 
INNER JOIN tbl2 t2 
ON tbl1.column_name=tbl2.column_name; 

SELECT column_name,column_name FROM tbl1 t1 WHERE tbl1.column_name IN (SELECT column_name FROM tbl2 t2 where t2.column_name = t1.column_name); 
0

NULL이 인덱싱되지 않을 수 있으므로 track_event.inboundid 및 temp_message.messageid가 null 일 수있는 경우 스캔과 관련이없는 액세스 계획을 작성할 수 없습니다.

인덱스가있는 경우에도 선택적인 것이 아니라면 그것을 사용하는 것이 가장 좋은 계획이라는 보장은 없습니다.

track_event 테이블/인덱스의 통계 란 무엇입니까?

0

튜닝 오류로 인한 문제 일 수 있습니다.

hashjoin을 비활성화하고 정렬하여 동작을 재현 할 수 있습니다. 충분히 큰 쿼리를 사용하면 더 큰 입력 데이터 세트에서 동일한 동작이 호출 될 수 있습니다. work_mem에 도달하면 가능합니다.

아래 설정

-- SET work_mem = 64 ; 
-- SET enable_material = 0; -- only needed for pg9.3 ? 
SET enable_hashjoin = 0 ; 
SET enable_sort = 0 ; 

effective_cache_sizerandom_page_cost이 영향 (아직)없는 것 같다 여기 (PG9.3beta) 영업의 쿼리 계획을 재현.

BTW : 질문 : (only 348 rows in track_event)은 seqscan이 다른 것보다 우위에 있음을 의미합니다. 계획에는 348 행이 필요합니다. 인덱스를 사용하여 얻을 수있는 선택성이 없습니다. (주 테이블은 메인 테이블에서 event_id 값을 얻는 데 필요합니다.)

따라서 프로덕션 DB의 문제는 work_mem 및 effective_cache_size를 사용 가능한 값으로 설정하면 해결할 수 있습니다. 물론 인덱스가 선택적인 경우에만 작동합니다. 100 %의 행을 검색하는 쿼리의 경우 인덱스가 간신히 유용합니다.