2012-10-03 2 views
5

У меня есть следующие таблицы/индексы -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 мс, то есть он не может использовать индекс. В любом случае, чтобы исправить это?

+0

Postgres использует планировщик, основанный на стоимости. Даже если он МОЖЕТ использовать индекс, это может быть не так быстро, как не использовать его. Вы можете играть с random_page_cost и cpu * cost vars, чтобы узнать, можете ли вы поговорить об этом с использованием этих индексов. Используйте объяснение, проанализируйте , чтобы увидеть, что он решает сделать и как быстро это происходит. –

+0

Использование индекса также зависит от доступной статистики. Сколько строк на самом деле имеет 'user_id = '3212312''? Вы сделали «вакуумный анализ» перед этим запросом (по крайней мере, после заполнения таблицы)? – wildplasser

+0

Чтобы увидеть, что он делает, когда индекс 'ix_coords' недоступен - может ли он использовать другой индекс и какова стоимость - попробуйте' BEGIN; DROP INDEX ix_coords На столе; EXPLAIN ANALYZE the_query; ROLLBACK; '. –

ответ

5

Я не знаю, может ли 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); 

, которая может привести к большому индексу, но может повысить этот запрос - если он работает. Имейте в виду, что поддержание такого индекса будет значительно замедляться INSERT и UPDATE s. Если вы уроните старый ix_coords, ваши запросы будут использовать ix_coords_and_user_id, даже если они не фильтруют по user_id, но это будет медленнее, чем ix_coords. Сохранение обоих приведет к ухудшению темпов INSERT и UPDATE.

См btree-gist


(устаревающие редактирования на вопрос, который изменяет вопрос полностью, когда написано пользователь имел многоколоночном индекс они теперь разделен на два отдельных из них):

Вы, кажется, не фильтруете или сортируете по user_id, только create_date. Pg не будет (не может?) Использовать только второй термин индекса с несколькими столбцами, например (user_id, create_date), ему также нужно использовать первый элемент.

Если вы хотите, чтобы индекс create_date, создайте для него отдельный индекс. Если вы используете и нуждаетесь в индексе (user_id, create_date) и обычно не используете только user_id, посмотрите, можете ли вы изменить порядок столбцов. Альтернативно создайте два независимых индекса: (user_id) и (create_date). Когда нужны оба столбца, Pg может комбинировать два независимых индекса, используя сканирование растрового индекса.

+0

жаль, что у меня были опечатки в моем вопросе, был смешанный id & user_id, в основном его просто «user_id». – kapso

+0

Я добавил результат анализа объяснения. Ценю вашу помощь. – kapso

+0

@ user310525 Вы, похоже, полностью изменили свои определения индексов, разделив компонент 'user_id'' ix_created_at' на новый индекс. Была ли старая ошибка? Или вы изменили настройку и не объяснили это? Если вы измените его, лучше объясните и добавьте новый материал, а не просто измените то, что там, поэтому старые ответы больше не имеют смысла в контексте. –

2

Я думаю, что Крейг правильно с его ответом, но я просто хотел бы добавить несколько вещей (и это не будет вписываться в комментарии)

Вы должны работать очень трудно силу PostgreSQL использовать индекс. Оптимизатор запросов интеллектуальный, и есть моменты, когда он будет считать, что сканирование последовательной таблицы будет быстрее. Обычно это правильно! :) Но вы можете играть с некоторыми настройками (например, seq_page_cost, random_page_cost и т. Д.), С которыми вы можете играть, чтобы попытаться использовать индекс. Вот ссылка на некоторые из configurations, которые вы можете изучить, если вы чувствуете, что не принимаете правильное решение. Но, опять же ... мой опыт в том, что большую часть времени Postgres умнее меня! :)

Надеюсь, это поможет вам (или кому-то в будущем).

Смежные вопросы