Почему используется только один индекс

У меня есть стол

CREATE TABLE timedevent
(
  id bigint NOT NULL,
  eventdate timestamp with time zone NOT NULL,
  newstateids character varying(255) NOT NULL,
  sourceid character varying(255) NOT NULL,
  CONSTRAINT timedevent_pkey PRIMARY KEY (id)
) WITH (OIDS=FALSE);

с ПК id.

Мне нужно запросить строки между двумя датами с определенным новым состоянием и источником из набора возможных источников.

Я создал индексы btree на eventdate и newstateids и еще один (хеш-индекс) на sourceid. Только индекс на date ускорил запросы - кажется, два других не используются. Почему это так? Как сделать запросы быстрее?

CREATE INDEX eventdate_index     ON timedevent USING btree (eventdate);
CREATE INDEX newstateids_index   ON timedevent USING btree (newstateids COLLATE pg_catalog."default");
CREATE INDEX sourceid_index_hash ON timedevent USING hash  (sourceid COLLATE pg_catalog."default");

Вот запрос, который генерирует Hibernate:

select this_.id as id1_0_0_, this_.description as descript2_0_0_, this_.eventDate as eventDat3_0_0_, this_.locationId as location4_0_0_, this_.newStateIds as newState5_0_0_, this_.oldStateIds as oldState6_0_0_, this_.sourceId as sourceId7_0_0_ 
from TimedEvent this_
where ((this_.newStateIds=? and this_.sourceId in (?, ?, ?, ?, ?, ?)))
    and this_.eventDate between ? and ?
    limit ?

Обновлено:
Извините за вводящий в заблуждение заголовок, но похоже, что посты используют все индексы. Проблема в том, что время моего запроса остается прежним. Вот план запроса, который у меня получился:

Limit  (cost=25130.29..33155.77 rows=321 width=161) (actual time=705.330..706.744 rows=279 loops=1)
  Buffers: shared hit=6 read=8167 written=61
  ->  Bitmap Heap Scan on timedevent this_  (cost=25130.29..33155.77 rows=321 width=161) (actual time=705.330..706.728 rows=279 loops=1)
        Recheck Cond: (((sourceid)::text = ANY ('{"root,kus-chemnitz,ize-159,Anwesend Bad","root,kus-chemnitz,ize-159,Alarmruf","root,kus-chemnitz,ize-159,Bett Alarm 1","root,kus-chemnitz,ize-159,Bett Alarm 2","root,kus-chemnitz,ize-159,Anwesend Zimmer" (...)
        Filter: ((eventdate >= '2017-11-01 15:41:00+01'::timestamp with time zone) AND (eventdate <= '2018-03-20 14:58:16.724+01'::timestamp with time zone))
        Buffers: shared hit=6 read=8167 written=61
        ->  BitmapAnd  (cost=25130.29..25130.29 rows=2122 width=0) (actual time=232.990..232.990 rows=0 loops=1)
              Buffers: shared hit=6 read=2152
              ->  Bitmap Index Scan on sourceid_index_hash  (cost=0.00..1403.36 rows=39182 width=0) (actual time=1.195..1.195 rows=9308 loops=1)
                    Index Cond: ((sourceid)::text = ANY ('{"root,kus-chemnitz,ize-159,Anwesend Bad","root,kus-chemnitz,ize-159,Alarmruf","root,kus-chemnitz,ize-159,Bett Alarm 1","root,kus-chemnitz,ize-159,Bett Alarm 2","root,kus-chemnitz,ize-159,Anwesend Z (...)
                    Buffers: shared hit=6 read=26
              ->  Bitmap Index Scan on state_index  (cost=0.00..23726.53 rows=777463 width=0) (actual time=231.160..231.160 rows=776520 loops=1)
                    Index Cond: ((newstateids)::text = 'ACTIV'::text)
                    Buffers: shared read=2126
Total runtime: 706.804 ms

После создания индекса с использованием btree on (sourceid, newstateids), как предложено a_horse_with_no_name, стоимость уменьшилась:

Limit  (cost=125.03..8150.52 rows=321 width=161) (actual time=13.611..14.454 rows=279 loops=1)
  Buffers: shared hit=18 read=4336
  ->  Bitmap Heap Scan on timedevent this_  (cost=125.03..8150.52 rows=321 width=161) (actual time=13.609..14.432 rows=279 loops=1)
        Recheck Cond: (((sourceid)::text = ANY ('{"root,kus-chemnitz,ize-159,Anwesend Bad","root,kus-chemnitz,ize-159,Alarmruf","root,kus-chemnitz,ize-159,Bett Alarm 1","root,kus-chemnitz,ize-159,Bett Alarm 2","root,kus-chemnitz,ize-159,Anwesend Zimmer","r (...)
        Filter: ((eventdate >= '2017-11-01 15:41:00+01'::timestamp with time zone) AND (eventdate <= '2018-03-20 14:58:16.724+01'::timestamp with time zone))
        Buffers: shared hit=18 read=4336
        ->  Bitmap Index Scan on src_state_index  (cost=0.00..124.95 rows=2122 width=0) (actual time=0.864..0.864 rows=4526 loops=1)
              Index Cond: (((sourceid)::text = ANY ('{"root,kus-chemnitz,ize-159,Anwesend Bad","root,kus-chemnitz,ize-159,Alarmruf","root,kus-chemnitz,ize-159,Bett Alarm 1","root,kus-chemnitz,ize-159,Bett Alarm 2","root,kus-chemnitz,ize-159,Anwesend Zimmer (...)
              Buffers: shared hit=18 read=44
Total runtime: 14.497 ms"

Пожалуйста, редактировать ваш вопрос и добавьте использованные вами операторы точныйcreate index и план выполнения, сгенерированный с помощью explain (analyze, buffers). Форматированный текст пожалуйста, нет скриншотов

a_horse_with_no_name 21.03.2018 08:28

<s> Потому что одновременно можно использовать только один индекс. </s>

zerkms 21.03.2018 08:29

@zerkms: это неправда.

a_horse_with_no_name 21.03.2018 08:29

@a_horse_with_no_name для этого самого запроса можно использовать более одного индекса для поиска?

zerkms 21.03.2018 08:30

Postgres имеет возможность использовать, например, сканирование индекса битовой карты для условия IN и другого индекса для условия between. Имеет ли это смысл и действительно ли планировщик решит это сделать, зависит от множества различных факторов. Но общее утверждение «используется только один индекс» неверно.

a_horse_with_no_name 21.03.2018 08:31

Один (btree) индекс на (newstateids, sourceid) может быть лучшим выбором, чем два одностолбцовых индекса.

a_horse_with_no_name 21.03.2018 08:33

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

zerkms 21.03.2018 08:35

@zerkms: да, если это означает создание индексов меньшего или меньшего размера (например, потому что эти индексы могут использоваться для большего количества запросов, чем только один)

a_horse_with_no_name 21.03.2018 08:36

Как я уже сказал: покажите, пожалуйста, план выполнения, созданный с помощью explain (analyze, buffers), а не просто вывод explain.

a_horse_with_no_name 21.03.2018 11:20

Извините за отсутствие (анализ, буферы). Теперь я знаю, что вы имеете в виду, попробовал индексировать (sourceid, newstateids) и это в 2 раза быстрее, чем раньше.

Veselin 21.03.2018 12:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
150
1

Ответы 1

В основном используется только один индекс, потому что база данных должна объединить ваши индексы в один, чтобы они были полезны (или объединить результаты поиска по большему количеству индексов), и это настолько дорого, что в этом случае она предпочитает не использовать и использовать только один из индексов, относящихся к одному предикату, и проверять другие предикаты непосредственно на значениях в найденных строках.

Один индекс B-дерева с несколькими столбцами будет работать лучше, как предлагает a_horse_with_no_name в комментариях. Также обратите внимание, что порядок столбцов имеет большое значение (столбцы, которые используются для поиска по одному значению, должны быть первыми, один для поиска по диапазону позже, вы хотите, чтобы ограничить диапазон поиска как можно больше). Затем databese будет проходить через индекс, ища строки, которые удовлетворяют предикату, используя первый столбец индекса (надеюсь, сужая количество строк), а второй столбец и второй предикат вступают в игру, ...

Использование отдельных индексов B-дерева при объединении предикатов с помощью оператора AND не имеет смысла для базы данных, потому что ей пришлось бы использовать один индекс для выбора всех строк, которые удовлетворяют одному предикату, а затем ей пришлось бы использовать другой индекс, прочитать его блоки (где хранится индекс) с диска снова, только для того, чтобы добраться до строк, которые удовлетворяют условию, относящемуся к этому второму индексу, но, возможно, не другому условию. И если они его удовлетворяют, вероятно, дешевле просто загрузить строку после первого использования индекса и напрямую проверить другие предикаты, не используя индекс.

Другие вопросы по теме