Я выполняю обновление, где мне требуется точное равенство для tstzrange
переменной. Изменено ~ 1M строк, а запрос занимает ~ 13 минут. Результат EXPLAIN ANALYZE
можно увидеть здесь , и фактические результаты чрезвычайно отличаются от тех, которые оцениваются планировщиком запросов. Проблема заключается в том, что при сканировании индекса t_range
ожидается возврат одной строки.
Похоже, это связано с тем, что статистика по типам диапазонов хранится не так, как статистика других типов. Глядя на pg_stats
представление для столбца, n_distinct
это -1, а другие поля (например most_common_vals
, most_common_freqs
) пусты.
Тем не менее, где-то должна храниться статистика t_range
. Чрезвычайно похожее обновление, где я использую 'inside' для 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).
Общие результаты были намного быстрее (в несколько раз). Я предполагаю, что это из-за удаления индексов, в соответствии с ответом Эрвина .
В этом тестовом примере запросы 1 и 2 в основном занимали одинаковое количество времени, поскольку они оба использовали объединение слиянием. То есть я не смог вызвать то, что заставило Postgres выбрать хеш-соединение, поэтому у меня нет ясности относительно того, почему Postgres выбрал плохо работающее хеш-соединение.
(lower(t_range),upper(t_range))
поскольку вы проверяете равенство.
(a = b)
два «содержит» условия:(a @> b AND b @> a)
? Меняется ли план?