我正在执行更新,其中我需要对tstzrange
变量进行完全相等的处理。约100万行被修改,查询耗时约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)
怎么办?计划会改变吗?