tstzrange
변수 에서 정확한 동등성이 필요한 업데이트를 수행하고 있습니다. ~ 1M 행이 수정되고 쿼리는 ~ 13 분이 걸립니다. 의 결과는 여기 에서 EXPLAIN ANALYZE
볼 수 있으며 실제 결과는 쿼리 플래너가 추정 한 결과와 매우 다릅니다. 문제는 인덱스 스캔시 단일 행이 리턴 될 것으로 예상한다는 것입니다.t_range
이것은 범위 유형에 대한 통계가 다른 유형의 통계와 다르게 저장된다는 사실과 관련이있는 것 같습니다. 상기 찾고 pg_stats
열의보기 n_distinct
-1 및 기타 분야 (예를 들어 most_common_vals
, most_common_freqs
) 비어 있습니다.
그러나 t_range
어딘가에 통계가 저장되어 있어야합니다 . t_range에서 '동일'을 사용하는 정확한 동등성 대신 매우 유사한 업데이트를 수행하는 데 약 4 분이 소요되며, 실질적으로 다른 쿼리 계획을 사용합니다 ( 여기 참조 ). 임시 테이블의 모든 행과 히스토리 테이블의 상당 부분이 사용되므로 두 번째 쿼리 계획이 의미가 있습니다. 더 중요한 것은 쿼리 플래너가에 대한 필터의 대략적인 행 수를 예측한다는 것입니다 t_range
.
분포 t_range
는 조금 이례적입니다. 이 테이블을 사용하여 다른 테이블의 기록 상태를 저장하고 다른 테이블의 변경 사항이 큰 덤프에서 한 번에 발생하므로의 고유 값이 많지 않습니다 t_range
. 다음은 각각의 고유 한 값에 해당하는 개수입니다 t_range
.
t_range | count
-------------------------------------------------------------------+---------
["2014-06-12 20:58:21.447478+00","2014-06-27 07:00:00+00") | 994676
["2014-06-12 20:58:21.447478+00","2014-08-01 01:22:14.621887+00") | 36791
["2014-06-27 07:00:00+00","2014-08-01 07:00:01+00") | 1000403
["2014-06-27 07:00:00+00",infinity) | 36791
["2014-08-01 07:00:01+00",infinity) | 999753
t_range
위의 구별에 대한 카운트 가 완료되었으므로 카디널리티는 ~ 3M입니다 (이 중 ~ 1M은 두 업데이트 쿼리의 영향을받습니다).
쿼리 1이 쿼리 2보다 훨씬 성능이 떨어지는 이유는 무엇입니까? 제 경우에는 쿼리 2가 좋은 대안이지만 정확한 범위 평등이 실제로 필요한 경우 Postgres가 더 똑똑한 쿼리 계획을 사용하도록하려면 어떻게해야합니까?
인덱스가있는 테이블 정의 (관련없는 열 삭제) :
Column | Type | Modifiers
---------------------+-----------+------------------------------------------------------------------------------
history_id | integer | not null default nextval('gtfs_stop_times_history_history_id_seq'::regclass)
t_range | tstzrange | not null
trip_id | text | not null
stop_sequence | integer | not null
shape_dist_traveled | real |
Indexes:
"gtfs_stop_times_history_pkey" PRIMARY KEY, btree (history_id)
"gtfs_stop_times_history_t_range" gist (t_range)
"gtfs_stop_times_history_trip_id" btree (trip_id)
쿼리 1 :
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range = '["2014-08-01 07:00:01+00",infinity)'::tstzrange;
쿼리 2 :
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND '2014-08-01 07:00:01+00'::timestamptz <@ sth.t_range;
Q1은 999753 개의 행을 업데이트하고 Q2는 999753 + 36791 = 1036544를 업데이트합니다 (즉, 임시 테이블은 시간 범위 조건과 일치하는 모든 행이 업데이트되도록합니다).
@ypercube의 의견 에 대한 응답 으로이 쿼리를 시도했습니다 .
쿼리 3 :
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range <@ '["2014-08-01 07:00:01+00",infinity)'::tstzrange
AND '["2014-08-01 07:00:01+00",infinity)'::tstzrange <@ sth.t_range;
쿼리 계획과 결과 ( 여기 참조 )는 이전 두 사례 사이에서 중간 정도였습니다 (~ 6 분).
2016/02/05 편집
1.5 년 후에 더 이상 데이터에 액세스 할 수 없으므로 동일한 구조 (인덱스 없음)와 유사한 카디널리티로 테스트 테이블을 만들었습니다. jjanes의 답변 은 원인이 업데이트에 사용되는 임시 테이블의 순서 일 수 있다고 제안했습니다. track_io_timing
(Amazon RDS 사용)에 액세스 할 수 없기 때문에 가설을 직접 테스트 할 수 없습니다 .
전반적인 결과는 훨씬 빨랐습니다 (여러 요인에 의해). 나는 이것이 Erwin의 대답 과 일치하는 인덱스 제거 때문이라고 추측합니다 .
이 테스트 사례에서는 쿼리 1과 쿼리 2가 모두 병합 조인을 사용했기 때문에 기본적으로 같은 시간이 걸렸습니다. 즉, Postgres가 해시 조인을 선택하게 한 원인을 트리거 할 수 없었기 때문에 Postgres가 왜 성능이 좋지 않은 해시 조인을 선택했는지 명확하지 않습니다.
(lower(t_range),upper(t_range))
평등을 확인한 후 일반 btree 인덱스를 추가하는 것 입니다.
(a = b)
을 두 개의 "포함"조건으로 변환하면(a @> b AND b @> a)
어떻게됩니까? 계획이 변경됩니까?