2013-10-05 5 views
4

Мне нужна помощь в оптимизации этого запроса.Оптимизация порядка заказа по запросу соединения

SELECT messages.* 
    FROM messages 
    INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

Без лимита этот запрос возвращает 200 тыс. Строк и занимает около 1,3-2 секунды для запуска. Проблема, по-видимому, в порядке выполнения. Без него запрос занимает .0005 секунд.

Indexes: 
    (subscription.user_id, subscription.entity_id) 
    (subscription.entity_id) 
    (messages.timestamp) 
    (messages.entity_id, messages.timestamp) 

я был в состоянии улучшить производительность, изменив запрос следующим образом:

SELECT messages.* FROM messages 
INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
INNER JOIN ( 
    SELECT message_id FROM messages ORDER BY timestamp DESC 
) as temp on temp.messsage_id = messages.message_id 
WHERE subscription.user_id = 1 LIMIT 50 

Это работает в .12 секунды. Очень приятное улучшение, но я хотел бы знать, может ли это быть лучше. Кажется Если бы я мог каким-то образом отфильтровать второе внутреннее соединение, тогда все будет быстрее.

Спасибо.

SCHEMA:

messages 
     message_id, entity_id, message, timestamp 

    subscription 
     user_id, entity_id 

UPDATE

Ответ Раймонд Nijland в решает мою исходную задачу, а другие просто всплыл

SELECT messages.* 
    FROM messages 
    STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

Прямого соединение является неэффективным в двух случаях:

  1. нет запись user_id в таблице подписки

  2. есть несколько соответствующих записей в таблице сообщений

Любые предложения о том, как это исправить? если не с точки зрения запроса, одно приложение?

UPDATE

EXPLAIN INFO

LIMIT 50

| id | select_type | table    | type | possible_keys       | key   | key_len | ref         | rows | Extra  | 
| 1 | SIMPLE  | messages   | index | idx_timestamp       | idx_timestamp | 4  | NULL         | 50 |    | 
| 1 | SIMPLE  | subscription  | eq_ref | PRIMARY,entity_id,user_id    | PRIMARY  | 16  | const, messages.entity_id    | 1 | Using index | 

Без предела

| id | select_type | table    | type | possible_keys       | key   | key_len | ref         | rows | Extra   | 
| 1 | SIMPLE  | messages   | ALL | entity_id_2,entity_id     | NULL   | NULL | NUL         | 255069 | Using filesort| 
| 1 | SIMPLE  | subscription  | eq_ref | PRIMARY,entity_id,user_id    | PRIMARY  | 16  | const, messages.entity_id    |  1 | Using index | 

СОЗДАТЬ ТАБЛИЦА ЗАЯВЛЕНИЙ:

С ~ 5000 строк

subscription | CREATE TABLE `subscription` (
    `user_id` bigint(20) unsigned NOT NULL, 
    `entity_id` bigint(20) unsigned NOT NULL, 
    PRIMARY KEY (`user_id`,`entity_id`), 
    KEY `entity_id` (`entity_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

с ~ 255,000 строк

messages | CREATE TABLE `messages` (
    `message_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `entity_id` bigint(20) unsigned NOT NULL, 
    `message` varchar(255) NOT NULL DEFAULT '', 
    `timestamp` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`message_id`), 
    KEY `entity_id` (`entity_id`,`timestamp`), 
    KEY `idx_timestamp` (`timestamp`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
+1

Вы можете разместить шоу создать заявление таблицы? –

+0

200 тысяч строк для одного пользователя? Вы уверены, что? –

+0

@ DanBracuk yes Я уверен –

ответ

3

падением индекса messages.entity_id это один избыточность и попробовать STRAIGHT_JOIN я думаю, MySQL оптимизатор экранные таблиц в заблуждении заказ. MySQL должен сначала получить доступ к табличным сообщениям, чтобы он мог использовать индекс для сообщений (entity_id, timestamp) и удалить необходимость «Использовать временный, с помощью файлового управления» (что медленно, если MySQL необходимо создать таблицу на дисках на основе MyISAM и необходимо сортировать (quicksort algoritm) это с чтением ввода-вывода диска и записью ввода-вывода).

SELECT STRAIGHT_JOIN messages.* 
    FROM messages 
    INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

ИЛИ

SELECT messages.* 
    FROM messages 
    STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

Я также имел эту проблему и я установил ее как этот http://sqlfiddle.com/#!2/b34870/1, но затем с таблицами Страна/Город

Edit, потому что от реакции Jason M на STRAIGHT_JOIN

Прямое соединение неэффективно в двух случаях:

нет записи user_id в таблице подписки

Действительно оптимизатор MySQL с INNER JOIN бы вызвать «Impossible WHERE заметил, после того, как не читают константные таблицы» и никогда не выполняет запрос. Но STRAIGHT_JOIN не запускает «Impossible WHERE, замеченный после чтения таблиц const», поэтому необходимо выполнить (возможно полный) сканирование индекса, чтобы найти его значение user_id, которое могло бы замедлить выполнение запроса. Easy Fix будет: использовать существующие User_ID года с STRAIGHT_JOIN

есть несколько соответствующих записей в таблице сообщений

Возможно такой же проблема здесь MySQL считает, что он должен делать (возможно полный) сканирование индекса в найти результаты. но мне нужно, чтобы увидеть EXPLAIN заявления, чтобы знать наверняка

Вы также можете захотеть попробовать этот запрос первого

SELECT 
* 
FROM (

SELECT 
    entity_id 

FROM 
    subscriptions 

WHERE 
    subscription.user_id = 1 
) 
subscriptions 

INNER JOIN 
messages 

ON 
subscriptions.entity_id = messages.entity_id 

ORDER BY 
messages.timestamp DESC 

LIMIT 50 
+1

Filesort не обязательно медленный. И это неправильное название, это не означает **, что это выполняется через файл на диске! –

+0

Я знаю ... Я имею в виду комбинацию «Использование временного, с помощью файлового управления», что может привести к сортировке временной таблицы на основе диска MyISAM с алгоритмом быстрой сортировки и множеством дисков IO –

+0

Спасибо, Раймонд, это абсолютно решило мою проблему. Запрос теперь выполняется в .000x секунд. –

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