Я здійснюю оновлення, де мені потрібна точна рівність tstzrange
змінної. ~ 1М рядків змінено, і запит займає ~ 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
вище повні, тому кардинальність становить ~ 3М (з яких на ~ 1М впливатиме будь-який запит оновлення).
Чому запит 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 EDIT
Через 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)
:? Чи змінюється план?