Индекс для column1 = x AND column2 <= y ORDER BY column3 DESC - PullRequest
1 голос
/ 28 января 2012

Я гуглил, сделал 15 разных индексов разных порядков (включая interval_end, ticker, interval_start DESC) и ASC/DESC, и в конечном итоге единственным, который он использует, является idx_onemin_intervalstart.Я верю, что оператор <= убивает меня.

Я прочитал все руководства PostgreSQL об индексах и все еще озадачен, что это не пример.

QUERY

explain analyze 
SELECT open 
FROM  onemin_interval 
WHERE ticker = 'QQQ' 
  AND interval_end <= 1326810600000 
ORDER BY interval_start DESC 
LIMIT 19960;

СХЕМА СТОЛОВ

Таблица public.onemin_interval

   Column            |     Type      | Modifiers
---------------------+---------------+-----------
 interval_start      | numeric(13,0) | 
 interval_end        | numeric(13,0) |
 open                | numeric(10,2) |
 close               | numeric(10,2) |
 high                | numeric(10,2) |
 low                 | numeric(10,2) |
 volume_for_interval | bigint        |
 ticker              | character(10) |
 humantimeopen       | character(23) |
 humantimeclose      | character(23) |
 adlval              | bigint        |

Индексы

"idx_onemin_intervalend" btree (interval_end)
"idx_onemin_intervalend_intervalstart" btree (interval_end, interval_start)
"idx_onemin_intervalstart" btree (interval_start DESC)
"idx_onemin_ticker" btree (ticker)
"idx_onemin_ticker_intervalend" btree (ticker, interval_end)

ПЛАН ЗАПРОСОВ

Limit  (cost=0.00..10295.29 rows=19960 width=20) (actual time=581.856..1731.352 rows=19960 loops=1)
->  Index Scan Backward using idx_onemin_intervalstart on onemin_interval  (cost=0.00..36843.32 rows=71430 width=20) (actual time=581.842..1621.713 rows=19960 loops=1)
     Filter: ((interval_end <= 1326810600000::numeric) AND (ticker = 'QQQ'::bpchar))
Total runtime: 1791.594 ms
(4 rows)

Новый контент после комментария

Я добавил тонну индексов для целей тестирования и запустил ANALYZE onemin_interval.Запрос почти такой же, как и раньше:

explain analyze
SELECT open
FROM  onemin_interval
WHERE ticker = 'QQQ'
  AND interval_end <= 1327698068642
ORDER BY interval_start DESC
LIMIT 19960;   

ПЛАН ЗАПРОСОВ

Limit  (cost=0.00..5849.68 rows=19960 width=16) (actual time=0.088..394.596 rows=19960 loops=1)
  ->  Index Scan using test11 on onemin_interval  (cost=0.00..21748.74 rows=74210 width=16) (actual time=0.079..298.848 rows=19960 loops=1)
     Filter: ((interval_end <= 1327698068642::bigint) AND (ticker = 'QQQ'::text))
Total runtime: 1442.898 ms
(4 rows)

СХЕМА

Таблица public.onemin_interval

   Column           |     Type      | Modifiers
--------------------+---------------+-----------
interval_start      | bigint        |
interval_end        | bigint        |
open                | numeric       |
close               | numeric       |
high                | numeric       |
low                 | numeric       |
volume_for_interval | bigint        |
ticker              | text          |
humantimeopen       | character(23) |
humantimeclose      | character(23) |
adlval              | bigint        |

Индексы

"idx_onemin_intervalend" btree (interval_end)
"idx_onemin_intervalend_intervalstart" btree (interval_end, interval_start)
"idx_onemin_intervalstart" btree (interval_start)
"idx_onemin_ticker" btree (ticker)
"idx_onemin_ticker_intervalend" btree (ticker, interval_end)
"test1" btree (interval_end DESC)
"test10" btree (ticker, interval_end DESC, interval_start DESC)
"test11" btree (interval_start DESC)
"test12" btree (interval_start DESC, interval_end DESC, ticker)
"test13" btree (interval_start DESC, ticker, interval_end DESC)
"test14" btree (ticker, interval_start DESC, interval_end DESC)
"test15" btree (interval_end DESC, interval_start DESC, ticker)
"test3" btree (interval_end)
"test4" btree (interval_end DESC, ticker)
"test5" btree (interval_end, ticker)
"test6" btree (ticker, interval_end DESC)
"test7" btree (ticker, interval_end)
"test8" btree (interval_end, ticker)
"test9" btree (interval_end DESC, ticker, interval_start DESC)

1 Ответ

0 голосов
/ 29 января 2012

Типы данных, размер и производительность

Прежде всего, тип символ (n) почти всегда является плохим выбором. Если у вас нет веских причин и вы знаете, что делаете, используйте вместо этого тип text. Если , вам действительно нужно установить максимальную длину, используйте CONSTRAINT. Пример для столбца ticker:

ALTER TABLE onemin_interval
ADD CONSTRAINT onemin_interval_ticker_len CHECK (length(ticker) < 11);

Очевидно, у вас есть такие значения, как 'QQQ', поэтому длина 10 кажется произвольной. Это уменьшит размер таблицы (возможно, значительно) и ускорит .. все.

Также рассмотрите возможность использования bigint вместо numeric(13,0). Вы не храните дробные цифры в любом случае, это, вероятно, лучший дизайн. numeric(13,0) требуется 13 байтов (5 + 4x2), bigint требуется 8 байтов и операции с ним выполняются быстрее во всех отношениях.

И наконец: могут ли столбцы bigint быть простыми integer тоже? (Если диапазон от -2147483648 до +2147483647 достаточно велик в течение срока службы таблицы.)

Основная проблема

Почему индекс idx_onemin_ticker_intervalend не используется?
Я бы попробовал несколько вещей:

Явное приведение

Если вы не можете изменить тип данных на text, протестируйте запрос с явным приведением к character(10):

WHERE ticker = 'QQQ'::character(10)

Старые версии PostgreSQL могут не использовать индекс для character(10) для незаполненной строки из трех символов ('QQQ'). Однако индекс равен , который использовался в PostgreSQL 8.4 и 9.1 в моих тестах.

Порядок столбцов в multi-colukmn index

Попробуйте изменить порядок столбцов в индексе нескольких столбцов.

idx_onemin_ticker_intervalend btree (interval_end DESC, ticker)

Порядок столбцов в многостолбцовом индексе соответствует . Хотя, в вашем случае, если бы все было в порядке, это должно быть неактуально , поскольку вы включаете условия в оба столбца. Я все еще проверю это.

Планировщик статистики

Планировщик запросов, по-видимому, ожидает, что условия ticker = 'QQQ' AND interval_end <= 1326810600000 будут не очень избирательными - хотя на самом деле они кажутся очень избирательными (результат 4 строки). Попробуйте поднять default_statistics_target для двух столбцов следующим образом:

ALTER TABLE onemin_ticker ALTER COLUMN interval_end SET STATISTICS 1000;
ALTER TABLE onemin_ticker ALTER COLUMN ticker SET STATISTICS 1000;
ANALYZE onemin_ticker;

Или даже больше, до 10000 (у вас есть 420k строк). По умолчанию установлено значение 100. Обычно это помогает только при неравномерном распределении данных, но при использовании огромных таблиц это должно легко окупаться.

Общая оптимизация производительности

Общие рекомендации по оптимизации производительности в PostgreSQL Wiki всегда применимы.


Наконец, имейте в виду, что этот тип запроса никогда не будет быстро светиться . Сортировка строк по 70 КБ занимает время - и движок должен отсортировать все строки 70 К, прежде чем можно будет применить LIMIT 19960.

Как видно из вашего обновления, планировщик запросов использует индекс test11 (interval_start DESC), который указывает, что сортировка является самой дорогой операцией в вашем запросе.

Поскольку вы выбираете по нескольким критериям перед сортировкой, я не вижу способа, как индекс может помочь вам в этом. Ваши данные не могут быть предварительно отсортированы и предварительно выбраны - за исключением , если предложение WHERE стабильно, то вы можете создать частичный индекс, такой как:

CREATE INDEX onemin_interval_interval_start_part_idx
  ON    onemin_interval (interval_start DESC)
  WHERE ticker = 'QQQ'::character(10) 
  AND   interval_end <= 1326810600000

... что приводит к осветлению жира. Но я не думаю, что тебе так повезло? Уже очень помогло бы, если бы один из критериев был стабильным, поэтому вы можете иметь частичный индекс по этому.

...