【发布时间】:2012-09-23 22:48:12
【问题描述】:
我有以下表格/索引 -
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_id 和 ix_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-80 毫秒左右。还从查询中获取order by created_at desc,又减少了 100 毫秒,这意味着它无法使用索引。无论如何要解决这个问题?
【问题讨论】:
-
Postgres 使用基于成本的计划器。即使它可以使用索引,它也可能不如不使用它快。您可以使用 random_page_cost 和 cpu* 成本变量来查看是否可以使用这些索引进行讨论。使用 explain analyze
查看它决定做什么以及它有多快。 -
索引的使用还取决于可用的统计信息。实际上有多少行
user_id='3212312'?在此查询之前(至少在填充表之后)您是否做过vacuum analyze? -
要查看当
ix_coords索引不可用时它会做什么——它是否可以使用其他索引以及成本是多少——试试BEGIN; DROP INDEX ix_coords ON thetable; EXPLAIN ANALYZE the_query; ROLLBACK;。
标签: postgresql postgis