Postgres结合多个索引

时间:2012-10-03 02:36:19

标签: postgresql postgis

我有以下表/索引 -

CREATE TABLE test
(
   coords geography(Point,4326), 
   user_id varchar(50), 
   created_at timestamp
);
CREATE INDEX ix_coords ON test USING GIST (coords);
CREATE INDEX ix_user_id ON test (user_id);
CREATE INDEX ix_created_at ON test (created_at DESC);

这是我想要执行的查询:

select * 
from updates 
where ST_DWithin(coords, ST_MakePoint(-126.4, 45.32)::geography, 30000) 
and user_id='3212312' 
order by created_at desc
limit 60

当我运行查询时,它只使用ix_coords索引。如何确保Postgres同时使用ix_user_idix_created_at索引进行查询?

这是一个新表,我在其中批量插入生产数据。 test表中的总行数: 15,069,489

我正在使用(effective_cache_size = 2GB)运行PostgreSQL 9.2.1(使用Postgis)。这是我的本地OSX,具有16GB RAM,Core i7 / 2.5 GHz,非SSD磁盘。

添加EXPLAIN ANALYZE输出 -

Limit  (cost=71.64..71.65 rows=1 width=280) (actual time=1278.652..1278.665 rows=60 loops=1)
   ->  Sort  (cost=71.64..71.65 rows=1 width=280) (actual time=1278.651..1278.662 rows=60 loops=1)
         Sort Key: created_at
         Sort Method: top-N heapsort  Memory: 33kB
         ->  Index Scan using ix_coords on test  (cost=0.00..71.63 rows=1 width=280) (actual time=0.198..1278.227 rows=178 loops=1)
               Index Cond: (coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography)
               Filter: (((user_id)::text = '4f1092000b921a000100015c'::text) AND ('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true))
               Rows Removed by Filter: 3122459
 Total runtime: 1278.701 ms

更新:

根据以下建议,我尝试使用cords + user_id索引:

CREATE INDEX ix_coords_and_user_id ON updates USING GIST (coords, user_id);

..但收到以下错误:

ERROR:  data type character varying has no default operator class for access method "gist"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.

更新:

所以CREATE EXTENSION btree_gist;解决了btree / gist复合索引问题。现在我的索引看起来像

CREATE INDEX ix_coords_user_id_created_at ON test USING GIST (coords, user_id, created_at);

注意:btree_gist不接受DESC / ASC。

新的查询计划:

Limit  (cost=134.99..135.00 rows=1 width=280) (actual time=273.282..273.292 rows=60 loops=1)
   ->  Sort  (cost=134.99..135.00 rows=1 width=280) (actual time=273.281..273.285 rows=60 loops=1)
         Sort Key: created_at
         Sort Method: quicksort  Memory: 41kB
         ->  Index Scan using ix_updates_coords_user_id_created_at on updates  (cost=0.00..134.98 rows=1 width=280) (actual time=0.406..273.110 rows=115 loops=1)
               Index Cond: ((coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) AND ((user_id)::text = '4e952bb5b9a77200010019ad'::text))
               Filter: (('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true))
               Rows Removed by Filter: 1
 Total runtime: 273.331 ms

查询比以前表现更好,几乎一秒钟更好,但仍然不是很好。我想这是我能得到的最好的?我希望在60-80ms左右。同时从查询中获取order by created_at desc,还可以消除另外100毫秒,这意味着它无法使用索引。无论如何要解决这个问题?

2 个答案:

答案 0 :(得分:5)

我不知道Pg是否可以将GiST索引和常规b树索引与位图索引扫描相结合,但我怀疑不是。您可能会在不向GiST索引添加user_id列的情况下获得最佳结果(并因此使其对于不使用user_id的其他查询更大更慢。)

作为一项实验,您可以:

CREATE EXTENSION btree_gist;
CREATE INDEX ix_coords_and_user_id ON test USING GIST (coords, user_id);

这可能会导致一个大索引,但可能会提升该查询 - 如果它有效。请注意,维护此类索引会显着降低INSERTUPDATE的速度。如果您删除旧版ix_coords,即使它们未在ix_coords_and_user_id上过滤,您的查询也会使用user_id,但它会慢于ix_coords。保持两者会使INSERTUPDATE减速更加糟糕。

请参阅btree-gist


通过编辑问题完全改变了问题;写入时,用户有多列索引,现在它们已分成两个单独的):

您似乎没有在user_id上进行过滤或排序,只有create_date。 Pg不会(不能?)只使用多列索引的第二项,如(user_id, create_date),它也需要使用第一项。

如果要索引create_date,请为其创建单独的索引。如果您使用并需要(user_id, create_date)索引,并且通常不单独使用user_id,请查看是否可以撤消列顺序。或者创建两个独立的索引(user_id)(create_date)。当需要两列时,Pg可以使用位图索引扫描来组合两个独立索引。

答案 1 :(得分:2)

我认为克雷格对他的答案是正确的,但我只想添加一些东西(并且它不适合评论)

你必须努力工作强制 PostgreSQL才能使用索引。查询优化器是智能的,有时它会相信顺序表扫描会更快。通常是对的! :)但是,你可以玩一些你可以玩的设置(比如seq_page_cost,random_page_cost等)来试着让它更喜欢索引。如果您认为没有做出正确的决定,可以链接到您可能想要检查的某些configurations。但是,再次......我的经验是,大多数时候,Postgres比我聪明! :)

希望这可以帮助你(或将来的某个人)。