Estou executando uma atualização em que exijo uma igualdade exata em uma tstzrange
variável. ~ 1M linhas são modificadas e a consulta leva ~ 13 minutos. O resultado de EXPLAIN ANALYZE
pode ser visto aqui , e os resultados reais são extremamente diferentes daqueles estimados pelo planejador de consultas. O problema é que a verificação do índice t_range
espera que uma única linha seja retornada.
Isso parece estar relacionado ao fato de que as estatísticas sobre os tipos de intervalo são armazenadas de maneira diferente das de outros tipos. Observando a pg_stats
vista da coluna, n_distinct
é -1 e outros campos (por exemplo most_common_vals
, most_common_freqs
) estão vazios.
No entanto, deve haver estatísticas armazenadas em t_range
algum lugar. Uma atualização extremamente semelhante, na qual eu uso um 'dentro' em t_range, em vez de uma igualdade exata, leva cerca de 4 minutos para executar e usa um plano de consulta substancialmente diferente (veja aqui ). O segundo plano de consulta faz sentido para mim, porque todas as linhas da tabela temporária e uma fração substancial da tabela de histórico serão usadas. Mais importante, o planejador de consultas prevê um número aproximadamente correto de linhas para o filtro ativado t_range
.
A distribuição de t_range
é um pouco incomum. Estou usando esta tabela para armazenar o estado histórico de outra tabela e as alterações na outra tabela ocorrem de uma só vez em grandes despejos, portanto, não há muitos valores distintos de t_range
. Aqui estão as contagens correspondentes a cada um dos valores exclusivos de 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
As contagens das distintas t_range
acima estão completas; portanto, a cardinalidade é de ~ 3M (dos quais ~ 1M serão afetados por uma consulta de atualização).
Por que a consulta 1 tem um desempenho muito pior do que a consulta 2? No meu caso, a consulta 2 é um bom substituto, mas se uma igualdade exata de intervalo fosse realmente necessária, como eu poderia fazer o Postgres usar um plano de consulta mais inteligente?
Definição de tabela com índices (eliminando colunas irrelevantes):
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)
Consulta 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;
Consulta 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;
O Q1 atualiza 999753 linhas e o Q2 atualiza 999753 + 36791 = 1036544 (ou seja, a tabela temporária é tal que todas as linhas correspondentes à condição de intervalo de tempo são atualizadas).
Eu tentei esta consulta em resposta ao comentário do @ ypercube :
Consulta 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;
O plano de consulta e os resultados (veja aqui ) foram intermediários entre os dois casos anteriores (~ 6 minutos).
05/02/2016 EDIT
Não tendo mais acesso aos dados após 1,5 anos, criei uma tabela de teste com a mesma estrutura (sem índices) e cardinalidade semelhante. A resposta de jjanes propôs que a causa poderia ser a ordem da tabela temporária usada para a atualização. Não pude testar a hipótese diretamente porque não tenho acesso track_io_timing
(usando o Amazon RDS).
Os resultados gerais foram muito mais rápidos (por um fator de vários). Suponho que isso se deva à remoção dos índices, consistente com a resposta de Erwin .
Nesse caso de teste, as consultas 1 e 2 basicamente levaram a mesma quantidade de tempo, porque ambas usaram a junção de mesclagem. Ou seja, eu não consegui acionar o que estava causando o Postgres escolher a junção de hash, portanto, não tenho clareza sobre por que o Postgres estava escolhendo a junção de hash com desempenho ruim em primeiro lugar.
(lower(t_range),upper(t_range))
desde que você verifique a igualdade.
(a = b)
em duas condições "contém"(a @> b AND b @> a)
:? O plano muda?