2016-01-01 3 views
2

В моей базе данных postgre у меня есть таблица с именем 'product'. В этой таблице у меня есть столбец с именем date_touched с типом timestamp. Я создал простой индекс postgre btree в этом столбце. это схема моего стола (я опустил нерелевантные определения столбца & индекса.)Почему этот простой запрос не использует индекс в postgres?

          Table "public.product" 
      Column   |   Type   | Modifiers        
---------------------------+--------------------------+------------------- 
id      | integer     | not null default nextval('product_id_seq'::regclass) 
date_touched    | timestamp with time zone | not null 

Indexes: 
    "product_pkey" PRIMARY KEY, btree (id) 
    "product_date_touched_59b16cfb121e9f06_uniq" btree (date_touched) 

Таблица содержит ~ 300000 строк, и я хочу, чтобы получить элемент из n-й таблицы заказанного date_touched. когда я хочу получить 1000-й элемент, он занимает 0.2 с, но когда я хочу получить 100 000-й элемент, это заняло около 6 секунд. Мой вопрос: почему требуется слишком много времени для извлечения 100 000-го элемента, пока я определил индекс btree?

это мои запросы с explain analyze, который показывает Postgres не использует ВТКЕЕ индекс и вместо этого рода все строки, чтобы найти элемент: 100000-

первый запрос (сотый элемент):

explain analyze SELECT product.id FROM product ORDER BY product.date_touched ASC LIMIT 1 OFFSET 1000; 

           QUERY PLAN 
----------------------------------------------------------------------------------------------------- 
Limit (cost=3035.26..3038.29 rows=1 width=12) (actual time=160.208..160.209 rows=1 loops=1) 
-> Index Scan using product_date_touched_59b16cfb121e9f06_uniq on product (cost=0.42..1000880.59 rows=329797 width=12) (actual time=16.651..159.766 rows=1001 loops=1) 
Total runtime: 160.395 ms 

второго запроса (100 000-й элемент):

explain analyze SELECT product.id FROM product ORDER BY product.date_touched ASC LIMIT 1 OFFSET 100000; 
           QUERY PLAN       
------------------------------------------------------------------------------------------------------ 
Limit (cost=106392.87..106392.88 rows=1 width=12) (actual time=6621.947..6621.950 rows=1 loops=1) 
    -> Sort (cost=106142.87..106967.37 rows=329797 width=12) (actual time=6381.174..6568.802 rows=100001 loops=1) 
     Sort Key: date_touched 
     Sort Method: external merge Disk: 8376kB 
     -> Seq Scan on product (cost=0.00..64637.97 rows=329797 width=12) (actual time=1.357..4184.115 rows=329613 loops=1) 
Total runtime: 6629.903 ms 
+1

Похож на аналогичную проблему, упомянутую здесь? Проверьте настройки Postgres и машины. http://www.postgresql.org/message-id/[email protected]m – Ashalynd

ответ

1

Это очень хорошо, что здесь используется SeqScan. Ваш OFFSET 100000 не очень хорош для IndexScan.

Немного теории

ВТКЕЕ индексы содержат 2 структуры внутри:

  1. сбалансированное дерево и
  2. двойной связанный список ключей.

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

Неправильно думать, что такие страницы сидят вместе на диске. На самом деле, более вероятно, что они распространены в разных местах. А для того, чтобы читать страницы по заказу индекса, система должна выполнять произвольные чтения дисков. Случайный диск IO стоит дорого, по сравнению с последовательным доступом. Поэтому хороший оптимизатор предпочтет вместо этого SeqScan.

Я настоятельно рекомендую “SQL Performance Explained” book, чтобы лучше понять индексы. Это также available on-line.

Что происходит?

Ваш пункт OFFSET приведет к тому, что база данных будет считывать связанный список указателей (вызывающих множество случайных чтений) и отбрасывать все эти результаты, пока вы не нажмете требуемое смещение. И хорошо, что Postgres решила использовать SeqScan + Sort здесь - это должно быть быстрее.

Вы можете проверить это предположение:

  • работает EXPLAIN (analyze, buffers) вашего big- OFFSET запроса
  • чем сделать SET enable_seqscan TO 'off';
  • и запустить EXPLAIN (analyze, buffers) снова, сравнивая результаты.

В общем, лучше избегать OFFSET, поскольку СУБД не всегда выбирают правильный подход здесь. (BTW, какую версию PostgreSQL вы используете?) Вот a comparison of how it performs для разных значений смещения.


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

  • показать первые N (скажем, 20) элементов
  • включают максимальную date_touched, который отображается на странице всех «Next» ссылки. Вы можете вычислить это значение на стороне приложения. Сделайте аналогичные для «Предыдущих» ссылок, за исключением минимальных date_touch.
  • на стороне сервера вы получите предельное значение. Поэтому, скажем, для «Далее» случае, вы можете сделать запрос следующим образом:
SELECT id 
    FROM product 
WHERE date_touched > $max_date_seen_on_the_page 
ORDER BY date_touched ASC 
LIMIT 20; 

Этот запрос позволяет наилучшим образом использовать индекс.

Конечно, вы можете настроить этот пример в соответствии с вашими потребностями. Я использовал разбивку на страницы, поскольку это типичный случай для OFFSET.

Еще одно примечание - запрос в 1 строку многократно, увеличение смещения для каждого запроса на 1 будет намного более трудоемким, чем выполнение одного пакетного запроса, который возвращает все эти записи, которые затем повторяются со стороны приложения ,

+0

так как я могу избежать смещения, как вы сказали? есть ли альтернативный запрос? также почему он не использует «сбалансированное дерево», чтобы найти n-й элемент? Я знаю, что такие структуры данных могут использоваться как для ключевых поисков, так и для нахождения n-го элемента (если он сохраняет количество детей в каждом узле дерева). есть ли такой индекс в postgresql для выполнения поиска n-го элемента в логарифмическом времени вместо линейного сканирования? (спасибо за ваш полный ответ!) – Ali

+0

@Ali, обновленный небольшим примером. – vyegorov

+0

спасибо за ваш пример. знаете ли вы, есть ли какой-либо индекс для postgresql, который отвечает на n-й элементный запрос, используя сбалансированное дерево в логарифмическом времени? (так же, как запрос ключевого поиска) – Ali

Смежные вопросы