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


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.