kap*_*pso 5 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);
Run Code Online (Sandbox Code Playgroud)
这是我想要执行的查询:
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
Run Code Online (Sandbox Code Playgroud)
当我运行查询时,它只使用ix_coords索引.如何确保Postgres 对查询使用ix_user_id和ix_created_at索引?
这是一个新表,我在其中批量插入生产数据.在总排test表:15069489
我正在使用(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
Run Code Online (Sandbox Code Playgroud)
更新:
根据以下建议,我尝试了索引+ user_id索引:
CREATE INDEX ix_coords_and_user_id ON updates USING GIST (coords, user_id);
Run Code Online (Sandbox Code Playgroud)
..但是得到以下错误:
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.
Run Code Online (Sandbox Code Playgroud)
更新:
所以CREATE EXTENSION btree_gist;解决了btree/gist复合索引问题.现在我的索引看起来像
CREATE INDEX ix_coords_user_id_created_at ON test USING GIST (coords, user_id, created_at);
Run Code Online (Sandbox Code Playgroud)
注意: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
Run Code Online (Sandbox Code Playgroud)
查询比以前表现更好,几乎一秒钟更好,但仍然不是很好.我想这是我能得到的最好的?我希望在60-80ms左右.同样order by created_at desc从查询中删除另外100ms,这意味着它无法使用索引.有任何解决这个问题的方法吗?
我不知道Pg是否可以将GiST索引和常规b树索引与位图索引扫描相结合,但我怀疑不是.您可以获得最佳结果,而无需user_id在GiST索引中添加列(并因此使其对于不使用的其他查询更大更慢user_id).
作为实验,您可以:
CREATE EXTENSION btree_gist;
CREATE INDEX ix_coords_and_user_id ON test USING GIST (coords, user_id);
Run Code Online (Sandbox Code Playgroud)
这可能会导致一个大的索引,但可能会提升该查询 - 如果它有效.请注意,保持这样的索引将显着减慢INSERT和UPDATEs.如果你删除旧的,ix_coords你的查询将使用,ix_coords_and_user_id即使它们没有过滤user_id,但它会慢于ix_coords.保持两者将使更慢INSERT和UPDATE更慢.
(通过编辑对问题完全改变问题;在编写用户时有多列索引,他们现在分成两个独立的索引):
你似乎并不被过滤或排序上user_id只,create_date.Pg不会(不能?)只使用多列索引的第二项(user_id, create_date),它也需要使用第一项.
如果要索引create_date,请为其创建单独的索引.如果您使用并需要(user_id, create_date)索引并且通常不user_id单独使用,请查看是否可以反转列顺序.或者创建两个独立的索引, (user_id)和(create_date).当需要两列时,Pg可以使用位图索引扫描组合两个独立索引.