У меня есть следующие таблицы/индексы -Postgres сочетающие несколько индексов
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
Я бегу PostgreSQL 9.2.1 (с PostGIS) с (effective_cache_size = 2 Гб). Это мой локальный OSX с 16 ГБ оперативной памяти, Core i7/2.5 ГГц, не-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
UPDATE:
на основе предложений ниже, я попытался индекс на шнурах + 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 * cost vars, чтобы узнать, можете ли вы поговорить об этом с использованием этих индексов. Используйте объяснение, проанализируйте, чтобы увидеть, что он решает сделать и как быстро это происходит. –
Использование индекса также зависит от доступной статистики. Сколько строк на самом деле имеет 'user_id = '3212312''? Вы сделали «вакуумный анализ» перед этим запросом (по крайней мере, после заполнения таблицы)? – wildplasser
Чтобы увидеть, что он делает, когда индекс 'ix_coords' недоступен - может ли он использовать другой индекс и какова стоимость - попробуйте' BEGIN; DROP INDEX ix_coords На столе; EXPLAIN ANALYZE the_query; ROLLBACK; '. –