2017-02-10 9 views
0

Я собираюсь объяснить себе, с помощью запроса, как это: (post_id = PRIMARY, blog_id = индекс)SQL Повышение эффективности: Ограничить количество FileSort

SELECT post_id FROM posts WHERE blog_id IN (2,3,...) ORDER BY post_id DESC LIMIT 10 

Update: идентификаторы в IN() может быть многочисленным. Если DB использует blog_id в качестве ключа для запроса, он должен сделать FileSort, так как индекс будет выглядеть следующим образом:

(blog_id,post_id)-> (1,55) (1,59) (1,69) (2,57) (2,71) (2,72) (3,12) 

Если вместо IN() вы ищете только один идентификатор blog_id = 2, ему не нужно делать никаких файлов, потому что все совпадения уже в порядке.

Проблема, с которой я думаю, что это происходит, а не на 100% уверенно, но просто глядя на время выполнения запросов, заключается в том, что если я добавлю LIMIT 10, эффективным способом будет только ловить и filesort последние 10 идентификаторов каждого blog_id, возможно, он уже делает это, но выглядит как IN (2,3,4) ORDER BY post_id DESC LIMIT 10, он filesorts тысяч идентификаторов вместо 30.

Я надеюсь, что я просто неверно, потому что, если я не это, это ужасная неэффективная ошибка. Если я прав, есть ли двигатель или изменение, которое я мог бы сделать? даже изменить базу данных. В настоящее время я на 10.1.13-MariaDB, а таблица InnoDB

+0

Действительно ли вы выполняете 'SELECT post_id', а не' SELECT * '? Это имеет существенное значение для этого вопроса. –

+0

Да, поскольку это подзапрос большего, где я выбираю * и присоединяется к другим таблицам, и уже в предыдущем вопросе год назад мне сказали, что эта форма была более эффективной для этого, и это было. http://stackoverflow.com/questions/30414641/avoid-filesort-with-inner-join-order-by – Vixxs

ответ

2

К сожалению, у MySQL нет индекса, который позволяет вам делать то, что вы хотите.

Однако, вы можете переписать запрос у вас есть и использовать существующий индекс:

SELECT p.post_id 
FROM ((SELECT post_id 
     FROM posts 
     WHERE blog_id = 2 
     ORDER BY post_id DESC 
     LIMIT 10 
    ) UNION ALL 
     (SELECT post_id 
     FROM posts 
     WHERE blog_id = 3 
     ORDER BY post_id DESC 
     LIMIT 10 
    ) 
    ) p 
ORDER BY post_id DESC 
LIMIT 10; 

Каждый вложенный запрос будет использовать индекс. И сортировка по 20 элементам довольно быстро.

+0

Ну, идентификаторы IN() могут быть сотнями, и они являются динамическими, они меняются, что было примером. С моей точки зрения, я думаю, что то, что я говорю, было бы легко и технически возможно, поэтому я не понимаю, почему это не сделано, не имеет смысла в моей голове. Кстати, у меня с MariaDB теперь есть новые движки для таблиц. Нет никакой возможности? Вы говорите, что нет индекса, который делает это, может быть, неправильно я понимаю порядок индекса? Поскольку индекс подходит для меня, проблема, которую я вижу здесь, - это способ, которым движет двигатель. – Vixxs

+0

PD: «И они динамичны, меняются» Я имел в виду изменения количества, которые можно было бы отсортировать, но, как я сказал, может быть много идентификаторов. – Vixxs

+1

Подход 'UNION' хорош для небольшого количества blog_ids; не очень хорошо для большого числа (N), накладные расходы «UNION», плюс таблица tmp будет 10 * N строк. –

1

Посмотрите на EXPLAIN SELECT ...; посмотрите, говорит ли он «filesort».

Выполните следующие действия, чтобы получить подробную информацию, даже для небольших наборов данных:

FLUSH STATUS; 
SELECT ...; 
SHOW SESSION STATUS LIKE 'Handler%'; 

Вам нужно INDEX(blog_id, post_id). Если вы используете InnoDB, а таблица имеет

PRIMARY KEY(post_id), 
INDEX(blog_id) 

, то у вас есть этот составной индекс. Это связано с тем, что каждый вторичный индекс неявно включает столбцы (ы) PK.

Поскольку вы используете MariaDB, посмотрите, будет ли LIMIT ROWS EXAMINED делать то, о чем вы просили.

Когда оптимизатор видит это:

WHERE blog_id IN (2,3) 
ORDER BY post_id DESC LIMIT 10 

и имеет как INDEX(blog_id) и INDEX(post_id), он принимает решение - но на ограниченной статистики - по какому пути идти:

План А : Filter on blog_id + filesort, или
План B: сканирование в порядке post_id, надеясь вскоре найти 10 строк.

Любой из них рискован. План A, если большинство или все строки (2,3), будут иметь большой вид. План B, когда насчитывается менее 10 совпадающих строк, сканирует всю таблицу (или индекс).

+0

Да, я старался заставлять первичный до и в зависимости от теста, в зависимости от количества сообщений, или если они слишком глубоки в индексе, один или другой способ лучше работает, мне придется сделать выбор, посмотрев на средний запрос. Но я предпочитаю не делать этого выбора и найти способ сделать то, что предлагает мой вопрос, что, по моему мнению, превзойдет эти два варианта почти во всех запросах. LIMIT ROWS EXAMINED не предназначен для этого, и он производит эту фатальную ошибку, если число должно быть низким: # 1028 - Сортировка отменена: – Vixxs

+0

Тогда я думаю, вам нужно переосмыслить «требования». Или, возможно, изменить «ожидания пользователей». Можете ли вы пожертвовать этим длинным списком? Заказ? Что-то другое? (Не все проблемы с производительностью могут быть решены, вы можете просто объяснить, но сложно решить проблему). –