Я использую PostgreSQL 9.1 на Ubuntu 12.04.
Мне нужно выбрать записи за определенный промежуток времени: в моей таблице time_limits
есть два timestamp
поля и одно integer
свойство. В моей фактической таблице есть дополнительные столбцы, которые не связаны с этим запросом.
create table (
start_date_time timestamp,
end_date_time timestamp,
id_phi integer,
primary key(start_date_time, end_date_time,id_phi);
Эта таблица содержит примерно 2 млн записей.
Такие запросы занимали огромное количество времени:
select * from time_limits as t
where t.id_phi=0
and t.start_date_time <= timestamp'2010-08-08 00:00:00'
and t.end_date_time >= timestamp'2010-08-08 00:05:00';
Поэтому я попытался добавить еще один индекс - инверсию PK:
create index idx_inversed on time_limits(id_phi, start_date_time, end_date_time);
У меня сложилось впечатление, что производительность улучшилась: время доступа к записям в середине таблицы кажется более разумным: где-то между 40 и 90 секундами.
Но это все еще несколько десятков секунд для значений в середине временного диапазона. И еще вдвое больше при наведении на конец таблицы (в хронологическом порядке).
Я впервые попытался explain analyze
получить этот план запроса:
Bitmap Heap Scan on time_limits (cost=4730.38..22465.32 rows=62682 width=36) (actual time=44.446..44.446 rows=0 loops=1)
Recheck Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
-> Bitmap Index Scan on idx_time_limits_phi_start_end (cost=0.00..4714.71 rows=62682 width=0) (actual time=44.437..44.437 rows=0 loops=1)
Index Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
Total runtime: 44.507 ms
Смотрите результаты на depesz.com.
Что я могу сделать, чтобы оптимизировать поиск? Вы можете видеть, сколько времени тратится на сканирование двух столбцов меток времени, если для id_phi
них установлено значение 0
. И я не понимаю большого сканирования (60K строк!) На отметках времени. Разве они не проиндексированы по первичному ключу, а idx_inversed
я добавил?
Должен ли я изменить тип отметки времени на что-то другое?
Я немного читал об индексах GIST и GIN. Я считаю, что они могут быть более эффективными при определенных условиях для пользовательских типов. Это жизнеспособный вариант для моего варианта использования?
источник
explain analyze
выходных данных, - это время, необходимое для запроса на сервере . Если ваш запрос занимает 45 секунд, то дополнительное время затрачивается на передачу данных из базы данных в программу, выполняющую запрос. В конце концов, это 62682 строки, и если каждая строка большая (например, имеет длинуvarchar
илиtext
столбцы), это может повлиять на время передачи коренным образом.rows=62682 rows
планировщика . Запрос возвращает 0 строк.(actual time=44.446..44.446 rows=0 loops=1)
Ответы:
Для Postgres 9.1 или новее:
В большинстве случаев порядок сортировки индекса вряд ли актуален. Postgres может сканировать назад практически так же быстро. Но для запросов диапазона по нескольким столбцам это может иметь огромное значение. Тесно связаны:
Рассмотрим ваш запрос:
Порядок сортировки первого столбца
id_phi
в индексе не имеет значения. Так как он проверен на равенство (=
), он должен стоять первым. Вы получили это право. Больше в этом связанном ответе:Postgres может быстро перейти к
id_phi = 0
следующему моменту и рассмотреть следующие два столбца соответствующего индекса. Они запрашиваются с условиями диапазона обратного порядка сортировки (<=
,>=
). В моем индексе квалифицирующие строки идут первыми. Должен быть самый быстрый способ с индексом B-Tree 1 :start_date_time <= something
: у индекса самая ранняя метка времени.Повторяйте до тех пор, пока первая строка не будет соответствовать требованиям (супер быстро).
end_date_time >= something
: индекс имеет самую последнюю метку времени первым.Продолжите со следующего значения для столбца 2 ..
Postgres может сканировать как вперед, так и назад. Как у вас был индекс, он должен прочитать все строки, соответствующие первым двум столбцам, а затем отфильтровать третий. Обязательно прочитайте главу Указатели и
ORDER BY
в руководстве. Это соответствует вашему вопросу довольно хорошо.Сколько строк соответствует первым двум столбцам?
Лишь немногие с
start_date_time
близко к началу временного диапазона таблицы. Но почти все строкиid_phi = 0
в хронологическом конце таблицы! Таким образом, производительность ухудшается с более поздним временем запуска.Планировщик оценок
Планировщик оценивает
rows=62682
ваш пример запроса. Никто из них не подходит (rows=0
). Вы можете получить более точные оценки, если увеличите целевой показатель для таблицы. Для 2.000.000 строк ...... может заплатить. Или даже выше. Больше в этом связанном ответе:
Я предполагаю, что вам это не нужно
id_phi
(только для нескольких отдельных значений, равномерно распределенных), но для временных отметок (множество различных значений, распределенных неравномерно).Я также не думаю, что это имеет большое значение с улучшенным индексом.
CLUSTER
/ pg_repackЕсли вы хотите быстрее, вы можете упорядочить физический порядок строк в вашей таблице. Если вы можете позволить себе блокировать вашу таблицу исключительно на короткий период времени (например, в нерабочие часы), чтобы переписать вашу таблицу и упорядочить строки в соответствии с индексом:
При одновременном доступе рассмотрим pg_repack , который может делать то же самое без исключительной блокировки.
В любом случае, эффект заключается в том, что из таблицы нужно читать меньше блоков и все предварительно отсортировано. Это единовременный эффект, ухудшающийся со временем, когда записи в таблицу фрагментируют физический порядок сортировки.
Индекс GiST в Postgres 9.2+
1 С pg 9.2+ есть еще один, возможно, более быстрый вариант: индекс GiST для столбца диапазона.
Существуют встроенные типы диапазонов для
timestamp
иtimestamp with time zone
:tsrange
,tstzrange
. Индекс btree обычно быстрее для дополнительногоinteger
столбца, напримерid_phi
. Меньше и дешевле в обслуживании тоже. Но запрос, вероятно, все еще будет быстрее в целом с объединенным индексом.Измените определение таблицы или используйте индекс выражения .
Для многоколоночного индекса GiST вам также понадобится установить дополнительный модуль
btree_gist
(один раз на базу данных), который предоставляет классы операторов для включенияinteger
.Trifecta! Многоколончатый функциональный индекс GiST :
Используйте «содержит диапазон» оператор
@>
в запросе Сейчас:SP-GiST индекс в Postgres 9,3+
SP-GiST индекс может быть даже быстрее , для такого рода запросов - за исключением того, что, цитирую инструкцию :
По-прежнему верно в Postgres 12.
Вы должны объединить
spgist
индекс только(tsrange(...))
со вторымbtree
индексом(id_phi)
. С добавленными накладными расходами я не уверен, что это может конкурировать.Связанный ответ с эталоном только для
tsrange
столбца:источник
Ответ Эрвина уже исчерпывающий, однако:
Типы диапазонов для временных меток доступны в PostgreSQL 9.1 с расширением Temporal от Джеффа Дэвиса: https://github.com/jeff-davis/PostgreSQL-Temporal
Примечание: имеет ограниченные возможности (использует Timestamptz, и вы можете иметь только стиль '[)', перекрывающий друг друга). Кроме того, есть много других веских причин для обновления до PostgreSQL 9.2.
источник
Вы можете попытаться создать многоколонный индекс в другом порядке:
Я однажды опубликовал аналогичный вопрос, также связанный с упорядочением индексов по многоколонному индексу. Ключ пытается использовать в первую очередь самые строгие условия, чтобы уменьшить пространство поиска.
Изменить : моя ошибка. Теперь я вижу, что у вас уже есть этот индекс.
источник
Bitmap Index Scan on idx_time_limits_phi_start_end
Мне удалось быстро увеличить (с 1 сек до 70мс)
У меня есть таблица с агрегацией многих измерений и многих уровней (
l
столбец) (30 с, 1 м, 1 ч и т. Д.), Есть два столбца с привязкой к диапазону:$s
для начала и$e
для конца.Я создал два многоколоночных индекса: один для начала и один для конца.
Я настроил запрос выбора: выберите диапазоны, где их начальная граница находится в заданном диапазоне. дополнительно выберите диапазоны, где их конечная граница находится в заданном диапазоне.
Explain показывает два потока строк, эффективно использующих наши индексы.
Индексы:
Выберите запрос:
Объясните:
Хитрость в том, что узлы вашего плана содержат только нужные строки. Ранее мы получили тысячи строк в узле плана, потому что он выбран
all points from some point in time to the very end
, затем следующий узел удалил ненужные строки.источник