선택 구별 속도를 높이는 방법?


16

일부 시계열 데이터에 대한 간단한 선택이 있습니다.

SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';

그리고 112 초가 걸립니다. 쿼리 계획은 다음과 같습니다.

http://explain.depesz.com/s/NTyA

내 응용 프로그램은 많은 별개의 연산을 수행해야하며 이와 같은 계산을해야합니다. 이런 종류의 데이터를 얻는 더 빠른 방법이 있습니까?

답변:


19

당신은 이것을 듣고 싶지 않을 수도 있지만, 속도를 높이는 가장 좋은 방법 SELECT DISTINCT은 시작 하지 않는 DISTINCT 것입니다. 더 나은 데이터베이스 디자인이나 더 나은 쿼리로 많은 경우에 (전부는 아님) 피할 수 있습니다.

때로는 GROUP BY다른 코드 경로가 필요하기 때문에 더 빠릅니다.

에서 특정 경우에 당신은 제거 할 수처럼, 그것은 보이지 않는다 DISTINCT. 그러나 이런 종류의 쿼리가 많은 경우 특수 인덱스로 쿼리를 지원할 수 있습니다.

CREATE INDEX foo ON events (project_id, "time", user_id);

추가 user_id는이 작업에서 인덱스 전용 스캔 을 얻는 경우에만 유용합니다 . 자세한 내용은 링크를 따르십시오. 쿼리 계획에서 값 비싼 비트 맵 힙 스캔 을 제거하면 쿼리 시간의 90 %가 소비됩니다.

귀하의 EXPLAIN결과에 따르면 쿼리는 50 만 개의 일치하는 행 중 2,491 명의 개별 사용자를 압축해야한다고 알려줍니다. 이것은 당신이 무엇을하든 초고속이되지는 않지만 실질적으로 더 빠를 수 있습니다.

쿼리의 시간 간격이 항상 같으면 MATERIALIIZED VIEW접는 user_id시간 (project_id, <fixed time intervall>)이 길어집니다. 그러나 시간 간격이 다양 할 가능성은 없습니다. 어쩌면 적어도 시간당 사용자 또는 다른 최소 시간 단위를 접을 수 있으며 상당한 오버 헤드를 보장하기에 충분한 성능을 구입할 수 있습니다.

Nitpick :
대부분의 술어는 다음과 "time"같아야합니다.

AND "time" >= '2015-01-11 8:00:00'
AND "time" <  '2015-02-10 8:00:00';


따로 : time식별자로 사용하지 마십시오 . 표준 SQL 의 예약어 이며 Postgres의 기본 유형입니다.


인덱스 전용 스캔에 ​​대해 조금 읽었습니다.
— Sam

불행히도, 시간 간격은 고정되어 있지 않습니다.
— Sam

@ Sam : 그렇다면 제안 된 색인으로 예제 쿼리가 얼마나 빨라 졌습니까?
— Erwin Brandstetter

3
@edwin : 아직 프로덕션을 시도하지 않았습니다. 그러나 로컬에서 동일한 데이터를 사용하여 원래 쿼리를 실행했으며 3678.780ms가 걸렸습니다. 그런 다음 인덱스를 추가하고 최대 170.156ms까지 속도를 높였습니다. 이제 계획에는 '이벤트에서 foo를 사용하여 인덱스 만 스캔'이 포함됩니다.
— Sam

1
@ 샘 : 니스! 그것이 내가 목표로 한 것입니다.
— Erwin Brandstetter

2

다음은 Sam의 사례와 Erwin의 답변에 대한 테스트입니다.

drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;

insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date 
limit 10000000

-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;

CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"         

-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;

어윈은 "이 말을 듣고 싶지는 않지만, SELECT DISTINCT 속도를 높이는 가장 좋은 방법은 DISTINCT로 시작하는 것을 피하는 것"이라고 말했다. " 나는 그가 옳다고 생각한다. "명확하고, 그룹별로, 순서대로"(있는 경우) 사용하지 말아야한다.

샘의 경우로 상황을 만났고 샘은 이벤트 테이블에서 파티션을 월 단위로 사용할 수 있다고 생각합니다. 쿼리 할 때 데이터 크기가 줄어들지 만 위 쿼리 대신 실행하려면 함수 (pl / pgsql)가 필요합니다. 이 함수는 query를 실행할 적절한 파티션 (조건에 따라 다름)을 찾습니다.


2
> 나는 그가 옳다고 생각한다. 우리는 "분별, 그룹, 순서"와 SELECT, INSERT, UPDATE를 피해야한다. 이러한 구성을 피하면 데이터베이스 속도가 매우 빠릅니다!
— greatvovan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.