2013-04-15 3 views
5

PostgreSQL 데이터베이스에 feeds_up이라는 테이블이 있습니다. 모양은 다음과 같습니다.날짜순, 카테고리 별 최신 기록 :

| feed_url | isup | hasproblems | observed timestamp with tz | id (pk)| 
|----------|------|-------------|-------------------------------|--------| 
| http://b.| t | f   | 2013-02-27 16:34:46.327401+11 | 15235 | 
| http://f.| f | t   | 2013-02-27 16:31:25.415126+11 | 15236 | 

5 분마다 ~ 20 행으로 늘어나는 300k 행과 비슷합니다. 쿼리가 매우 자주 실행됩니다 (모든 페이지로드)

select distinct on (feed_url) feed_url, isUp, hasProblems 
    from feeds_up 
    where observed <= '2013-02-27T05:38:00.000Z' 
    order by feed_url, observed desc; 

예를 들어 그 시간을 매개 변수로 지정했습니다. Explain 분석은 explain.depesz.com에 있습니다. 약 8s 걸립니다. 미친!

feed_url에는 약 20 개의 고유 값이 있으므로 실제로는 비효율적입니다. 나는 어리 석고 함수에서 FOR 루프를 시도 할 것이라고 생각했다. 단지 307ms 소요

CREATE OR REPLACE FUNCTION feedStatusAtDate(theTime timestamp with time zone) RETURNS SETOF feeds_up AS 
$BODY$ 
DECLARE 
    url feeds_list%rowtype; 
BEGIN 
FOR url IN SELECT * FROM feeds_list 
LOOP 
    RETURN QUERY SELECT * FROM feeds_up 
    WHERE observed <= theTime 
    AND feed_url = url.feed_url 
    ORDER BY observed DESC LIMIT 1; 
END LOOP; 
END; 
$BODY$ language plpgsql; 

select * from feedStatusAtDate('2013-02-27T05:38:00.000Z'); 

!

SQL에서 FOR 루프를 사용하면 잘못된 방법으로 문안합니다. 첫 번째 문법처럼 효율적인 쿼리를 작성하는 방법은 무엇입니까? 그게 가능하니? 아니면 FOR 루프가 실제로 가장 좋은 곳입니까?

ETA

포스트 그레스 버전 : PostgreSQL의 9.1.5 (수세 리눅스) 4.3.4 [GCC-4_3 분기 개정 152,973] gcc에서 컴파일는 i686-PC-리눅스 GNU에 32 비트 feeds_up에

인덱스 :

CREATE INDEX feeds_up_url 
    ON feeds_up 
    USING btree 
    (feed_url COLLATE pg_catalog."default"); 

CREATE INDEX feeds_up_url_observed 
    ON feeds_up 
    USING btree 
    (feed_url COLLATE pg_catalog."default", observed DESC); 

CREATE INDEX feeds_up_observed 
    ON public.feeds_up 
    USING btree 
    (observed DESC); 
+0

그냥 @Cathy가 'work_mem'을 20MB로 늘리려고했는데 http://explain.depesz.com/s/UJw (지금 삭제 한 답변에 대한 의견)입니다. 정렬은 더 이상 디스크로 유출되지 않지만 쿼리가 크게 빠르지는 않습니다. 인덱스 만들기'CREATE INDEX feeds_up_feed_url_observed ON feed_up (feed_url, 관찰 된 DESC);도 좋지 않았다; 색인은 사용되지 않습니다. –

+0

그런데 PostgreSQL 버전은 무엇입니까? 'SELECT 버전()'. –

+0

@CraigRinger 9.1.5, 편집을하겠습니다. – Cathy

답변

1

:

SELECT fu.feed_url, fu.isup, fu.hasproblems, fu.observed 
FROM feeds_up fu 
JOIN 
(
    SELECT feed_url, max(id) AS id FROM feeds_up 
    WHERE observed <= '2013-03-27T05:38:00.000Z' 
    GROUP BY feed_url 
) AS q USING (id); 
ORDER BY fu.feed_url, fu.observed desc; 

나는 신속한 테스트를 수행했는데 이것은 "관찰 된"유일한 지표를 사용하여 매우 효율적으로 작동합니다.

UPDATE :이에서 실행 내 시스템에서

SELECT DISTINCT ON (fu.feed_url) fu.feed_url, fu.isup, fu.hasproblems, fu.observed 
FROM feeds_up fu 
JOIN 
(
    SELECT feed_url, max(observed) as observed FROM feeds_up 
    WHERE observed <= '2013-03-27T05:38:00.000Z' 
    GROUP BY feed_url 
) AS q USING (feed_url, observed) 
ORDER BY fu.feed_url, fu.observed desc; 

을 :

는 다음과 같이 쿼리 이상 수정할 수 있습니다 (순서대로 삽입 할 수 없습니다 기록 때문에) 대신에 "ID"의 "관찰"을 사용하려면 "관찰 된"것에 대한 하나의 색인으로 거의 같은 시간. YMMV

+0

그게 내가 찾고 있던 바로 그거야! 핫 캐시를 사용하면 100ms 미만입니다. – Cathy

0

당신은 당신이 무엇을 indexs 설명해야 최적화에 대해 얘기합니다.

나는

"FEED_URL"에서 마지막으로 하나가 유용 할 수 있습니다 "FEED_URL가 관찰"절대적으로 필수 인덱스가

또 다른 지표가 될 것 "관찰"에있는 일을 생각하지만 난 그렇게하지 않다 이 사람이 선한 사람보다 더 따뜻할 지 확신 해. 물론이 모든 단점은 삽입에 대한 성능이 될 것이지만, 문제는 좀 더 잘 알아야 할 것입니다.

당신이 (당신이 말하는 이후에만 한정된 몇 가지를 가지고) "FEED_URL"에 의한 partition을 고려 있나요? 그렇지 않으면 "관찰 된"날짜 (월)로? "ID가"시리얼 항상 연속이라고 가정하면, 하위 쿼리의 각 FEED_URL의 MAX (ID)를 찾아 단순화하고 다음과 같이 데이터의 나머지 부분에서 당겨 수

+0

나는 가지고있는 색인을 추가하도록 편집했습니다. @CraigRinger는 위에서 말한 것처럼 결합 된 인덱스를 만들려고했지만 쿼리 플래너에서는 사용하지 않았습니다 (예,'vacuum analyze'd). – Cathy

+0

필자는 분할을 오해하지 않는 이상 feed_url의 파티션이 도움이 될 것이라고 생각하지 않습니다. 나는 "관찰 된"(기존 테이블에 대한 지시 사항을 찾을 수 없습니까?)으로 파티셔닝을 시도 할 의향이 있습니다. 그러나 한 달에 약 200k 행의 테이블에 대해서는 극단적 인 것으로 보입니다. – Cathy

관련 문제