У меня есть стол
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"
<s> Потому что одновременно можно использовать только один индекс. </s>
@zerkms: это неправда.
@a_horse_with_no_name для этого самого запроса можно использовать более одного индекса для поиска?
Postgres имеет возможность использовать, например, сканирование индекса битовой карты для условия IN и другого индекса для условия between. Имеет ли это смысл и действительно ли планировщик решит это сделать, зависит от множества различных факторов. Но общее утверждение «используется только один индекс» неверно.
Один (btree) индекс на (newstateids, sourceid) может быть лучшим выбором, чем два одностолбцовых индекса.
@a_horse_with_no_name в реальном мире, часто ли приходится полагаться на оптимизатор, чтобы он выполнял правильную работу и был достаточно умен, чтобы использовать несколько индексов вместо использования составного индекса? (не будучи язвительным, настоящий вопрос от разработчика эксперту по базам данных)
@zerkms: да, если это означает создание индексов меньшего или меньшего размера (например, потому что эти индексы могут использоваться для большего количества запросов, чем только один)
Как я уже сказал: покажите, пожалуйста, план выполнения, созданный с помощью explain (analyze, buffers), а не просто вывод explain.
Извините за отсутствие (анализ, буферы). Теперь я знаю, что вы имеете в виду, попробовал индексировать (sourceid, newstateids) и это в 2 раза быстрее, чем раньше.





В основном используется только один индекс, потому что база данных должна объединить ваши индексы в один, чтобы они были полезны (или объединить результаты поиска по большему количеству индексов), и это настолько дорого, что в этом случае она предпочитает не использовать и использовать только один из индексов, относящихся к одному предикату, и проверять другие предикаты непосредственно на значениях в найденных строках.
Один индекс B-дерева с несколькими столбцами будет работать лучше, как предлагает a_horse_with_no_name в комментариях. Также обратите внимание, что порядок столбцов имеет большое значение (столбцы, которые используются для поиска по одному значению, должны быть первыми, один для поиска по диапазону позже, вы хотите, чтобы ограничить диапазон поиска как можно больше). Затем databese будет проходить через индекс, ища строки, которые удовлетворяют предикату, используя первый столбец индекса (надеюсь, сужая количество строк), а второй столбец и второй предикат вступают в игру, ...
Использование отдельных индексов B-дерева при объединении предикатов с помощью оператора AND не имеет смысла для базы данных, потому что ей пришлось бы использовать один индекс для выбора всех строк, которые удовлетворяют одному предикату, а затем ей пришлось бы использовать другой индекс, прочитать его блоки (где хранится индекс) с диска снова, только для того, чтобы добраться до строк, которые удовлетворяют условию, относящемуся к этому второму индексу, но, возможно, не другому условию. И если они его удовлетворяют, вероятно, дешевле просто загрузить строку после первого использования индекса и напрямую проверить другие предикаты, не используя индекс.
Пожалуйста, редактировать ваш вопрос и добавьте использованные вами операторы точный
create indexи план выполнения, сгенерированный с помощьюexplain (analyze, buffers). Форматированный текст пожалуйста, нет скриншотов