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 index. Как я могу гарантировать, что 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

ОБНОВИТЬ:

Основываясь на приведенных ниже предложениях, я попробовал index on 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 мсек, что означает, что он не может использовать индекс. Как бы то ни было, чтобы это исправить?


person kapso    schedule 03.10.2012    source источник
comment
Postgres использует планировщик на основе затрат. Даже если он МОЖЕТ использовать индекс, это может быть не так быстро, как не использовать его. Вы можете поиграть с переменными random_page_cost и cpu * cost, чтобы увидеть, сможете ли вы использовать эти индексы. Используйте команду «Объясните, проанализируйте» <ваш запрос здесь>, чтобы узнать, что он решает сделать и насколько быстро это происходит.   -  person Scott Marlowe    schedule 03.10.2012
comment
Использование индекса также зависит от доступной статистики. Сколько строк на самом деле имеет user_id='3212312'? Выполняли ли вы vacuum analyze перед этим запросом (по крайней мере, после заполнения таблицы)?   -  person wildplasser    schedule 03.10.2012
comment
Чтобы узнать, что он делает, когда индекс ix_coords недоступен - может ли он использовать другой индекс и какова его стоимость, - попробуйте BEGIN; DROP INDEX ix_coords ON thetable; EXPLAIN ANALYZE the_query; ROLLBACK;.   -  person Craig Ringer    schedule 04.10.2012


Ответы (2)


Я не знаю, может ли 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 и UPDATEs. Если вы откажетесь от старого 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 может объединить два независимых индекса, используя сканирование индекса по битовой карте.

person Craig Ringer    schedule 03.10.2012
comment
извините, у меня были опечатки в моем вопросе, были смешанные id и user_id, в основном это просто user_id. - person kapso; 03.10.2012
comment
Я добавил вывод объяснения анализа. Ценю вашу помощь. - person kapso; 04.10.2012
comment
@ user310525 Похоже, вы полностью изменили определения индекса, разделив компонент ix_created_at user_id на новый индекс. Был ли старый просто неправильным? Или вы изменили настройку и не объяснили это? Если вы измените его, лучше объясните и добавьте новый материал, а не просто молча меняйте то, что там есть, так что старые ответы больше не имеют смысла в контексте. - person Craig Ringer; 04.10.2012
comment
PostgreSQL может использовать второй член многостолбцового индекса, но часто это дороже, чем полное игнорирование индекса и переход прямо к таблице. Дополнительную информацию см. В руководстве. - person willglynn; 04.10.2012
comment
@CraigRinger postgres не позволит мне создать индекс содержания на coords + user_id ?? Я обновил исходный вопрос. - person kapso; 04.10.2012
comment
@ user310525 Похоже, вам нужно установить btree_gist extension. CREATE EXTENSION btree_gist;. Хотя, честно говоря, я очень сомневаюсь, что вы многого добьетесь. - person Craig Ringer; 04.10.2012
comment
@CraigRinger, посмотрите мое обновление, индекс btree_gist действительно улучшил производительность запросов, большое спасибо. Поскольку мне нужна сортировка created_at, я создал составной индекс для coords + user_id + created_at. Но кажется, что это не влияет на порядок по, а сортировка по created_at занимает около 100 мс ?? :( - person kapso; 04.10.2012
comment
@ user310525 Я не думаю, что индекс поможет в этом вопросе. Что, если вы подбросите work_mem? Попробуйте SET work_mem = 2MB - person Craig Ringer; 04.10.2012
comment
@ user310525 Удаление неиспользуемого столбца в индексе сделает его быстрее. Вы также можете попробовать переупорядочить его как (user_id, coords) и посмотреть, работает ли это лучше. - person Craig Ringer; 04.10.2012

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

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

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

person David S    schedule 03.10.2012