2011-04-11 2 views
1

내가로 분류되는 게시물이있는 테이블이 있습니다PostgreSQL 속도가 느립니다. COUNT() - 유일한 해결책입니까?

  • 유형
  • 태그
  • 언어

사람들 "범주"의 모든 옆 테이블 (posts_types)와에 저장됩니다 다음 테이블 (posts_types_assignment)을 통해 연결됩니다.

COUNTing in PostgreSQL is really slow (나는이 테이블에 500k 개 이상의 레코드가 있습니다.) 그리고/tag/lang 유형의 조합으로 분류 된 게시물 수를 얻어야합니다.

트리거를 통해 문제를 해결할 수 있다면 많은 멀티 레벨 루프로 가득차 있습니다. 실제로는 좋지 않지만 유지 관리가 어렵습니다.

어떤 유형/태그/언어로 분류 된 게시물의 실제 수를 효과적으로 얻는 다른 해결책이 있습니까?

+0

쿼리 계획을 보려면 [explain] (http://www.postgresql.org/docs/9.0/static/sql-explain.html)을 수행 했습니까? –

+0

@Sam 네, 저도 그렇게 생각합니다 (많은 곳에서 발견 한 정보에 따르면) 이것은 PostgreSQL 자체의 문제입니다. 이제 "~ COUNT (*) FROM posts"를 시도했는데 ~ 1 500 000 레코드 였고 COUNTing은 ~ 9 초가 걸렸습니다. 두 번째로 (Postgre가 아마도 쿼리를 캐시하거나) 약 2 초가 걸렸다. 어쨌든 2 초는 매우 느립니다. –

+1

인덱스를 만들려고 했습니까? 부분적인 인덱스를 잊지 마라. 데이터의 하위 집합 만 인덱싱하면 큰 성능 차이가 발생할 수 있습니다. 이러한 명확한 작업을 수행했지만 여전히 개선되지 않으면 추가 도움말 (테이블 스키마, 샘플 데이터, 샘플 쿼리, 설명 계획, postgresql 버전 등)을 추가로 게시하는 것이 좋습니다. –

답변

1

나를 똑바로 세우자.

테이블이 posts입니다. 테이블이 posts_types입니다. 두 사람은 posts_types_assignment에 다 대다 참여자가 있습니다. 그리고 느린 쿼리가 있습니다.

SELECT count(*) 
FROM posts p 
    JOIN posts_types_assigment pta1 
    ON p.id = pta1.post_id 
    JOIN posts_types pt1 
    ON pt1.id = pta1.post_type_id 
     AND pt1.type = 'language' 
     AND pt1.name = 'English' 
    JOIN posts_types_assigment pta2 
    ON p.id = pta2.post_id 
    JOIN posts_types pt2 
    ON pt2.id = pta2.post_type_id 
     AND pt2.type = 'tag' 
     AND pt2.name = 'awesome' 

그리고 왜 그렇게 느린지 알고 싶습니다.

첫 번째 메모는 조인이 아닌 posts 테이블에 식별자가있는 경우 PostgreSQL이 훨씬 적은 작업을해야한다는 것입니다. 그러나 그것은 논쟁의 여지가 있습니다. 결정이 내려졌습니다.

필자가 더 유용한 점은 PostgreSQL에 Oracle과 유사한 쿼리 최적화 프로그램이 있다고 생각합니다. 이 경우 고려해야 할 가능한 쿼리 계획의 조합 폭발을 제한하기 위해 일부 테이블로 시작하여 한 번에 하나 이상의 데이터 집합에 반복적으로 조인하는 계획 만 고려합니다. 그러나 이러한 쿼리 계획은 여기에서 작동하지 않습니다. pt1으로 시작하여 1 레코드를 얻은 다음 pta1으로 이동하여 여러 레코드를 얻고 p에 가입하고 동일한 레코드 수를 얻은 다음 pta2에 가입하면 엄청난 수의 레코드를 얻은 다음 pt2, 몇 개의 레코드 만 가져옵니다. pta2에 가입하는 것은 느린 단계입니다. 데이터베이스에서 원하는 레코드를 모르기 때문에 게시물과 메타 데이터 (유형, 언어 또는 태그)의 모든 조합에 대해 임시 결과 집합을 만들어야하기 때문입니다.

이것이 실제로 문제가되는 경우 오른쪽 계획은 다음과 같습니다. pt1pta1에 가입 시키십시오. pt2pta2에 가입시킨 다음 첫 번째 쿼리의 결과에 참여한 다음 p에 가입하십시오. 그런 다음 계산하십시오. 이것은 거대한 결과 세트를 얻지 못한다는 것을 의미합니다.

이 경우, 쿼리 최적화 프로그램에게 새로운 유형의 실행 계획을 생각하게하려면 쿼리 최적화 프로그램에 알릴 방법이 없습니다. 그러나 그것을 강제하는 방법이 있습니다.

CREATE TEMPORARY TABLE t1 
AS 
SELECT pta* 
FROM posts_types pt 
    JOIN posts_types_assignment pta 
    ON pt.id = pta.post_type_id 
WHERE pt.type = 'language' 
    AND pt.name = 'English'; 

CREATE INDEX idx1 ON t1 (post_id); 

CREATE TEMPORARY TABLE t2 
AS 
SELECT pta* 
FROM posts_types pt 
    JOIN posts_types_assignment pta 
    ON pt.id = pta.post_type_id 
    JOIN t1 
    ON t1.post_id = pta.post_id 
WHERE pt.type = 'language' 
    AND pt.name = 'English'; 

SELECT COUNT(*) 
FROM posts p 
    JOIN t1 
    ON p.id = t1.post_id; 

임의의 오타 등을 배제하면이 기능이 다소 향상 될 수 있습니다. 그렇지 않으면 테이블의 색인을 다시 확인하십시오.btilly 노트로

1

, 그가 스키마를 추측 제대로 경우, 테이블 디자인은 도움이되지 않습니다 - 그것은 즉, 예를 들어, 세 개의 테이블 posts_tag(post_id,tag)post_lang(post_id,lang)post_type(post_id,type)가 더 자연스러운 것을 가지고 (적어도 첫눈에) 보인다 훨씬 더 효율적입니다.

그 외 (또는 그 외에도) (lang,type,tag,nposts) 열을 사용하여 가능한 모든 계산을 요약 한 테이블 또는 구체화 된보기를 생각할 수 있습니다. 물론, 이것을 전체로 계산하는 것은 매우 느릴 수 있지만 (처음은 따로하고) 백그라운드에서 전체적으로 수행 할 수도 있고, 일정 간격으로 수행 할 수도 있습니다 (데이터가 많이 변경되지 않는 경우, 정확한 카운트 필요), 또는 열성적으로 트리거. 예를 참조하십시오. here

관련 문제