оптимизация порядка запросов по результатам на использование файловой сортировки; - PullRequest
2 голосов
/ 16 марта 2012

Запрос:

    SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        pm_replies as r
        LEFT JOIN users as u
            ON u.uid = r.uid
    WHERE
        r.msg_id = '784351921943772258'

    ORDER BY r.date DESC

Я перепробовал все комбинации индексов, которые мог придумать, искал в Google, как лучше всего проиндексировать это, но ничего не получилось.

этот запрос занимает 0, 33 на 500 возвращенных предметов и подсчет ...


ОБЪЯСНЕНИЕ:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  r   ALL     index1  NULL    NULL    NULL    540     Using where; Using filesort
1   SIMPLE  u   eq_ref  uid     uid     8   site.r.uid  1   

ПОКАЗАТЬ СОЗДАТЬ pm_replies

CREATE TABLE `pm_replies` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `reply_id` bigint(20) NOT NULL,
 `msg_id` bigint(20) NOT NULL,
 `uid` bigint(20) NOT NULL,
 `body` text COLLATE utf8_unicode_ci NOT NULL,
 `date` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `index1` (`msg_id`,`date`,`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=541 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

ПОКАЗАТЬ СОЗДАТЬ пользователей

CREATE TABLE `users` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `uid` bigint(20) NOT NULL,
 `username` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
 `email` text CHARACTER SET latin1 NOT NULL,
 `password` text CHARACTER SET latin1 NOT NULL,
 `profile_picture` text COLLATE utf8_unicode_ci NOT NULL,
 `date_registered` datetime NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `uid` (`uid`),
 UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM AUTO_INCREMENT=2004 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

Ответы [ 5 ]

5 голосов
/ 16 марта 2012

Для запроса, как он есть, лучшие индексы выглядят как ...

pm_replies: (msg_id, date, uid)
users:      (uid)

Важным является pm_replies.Вы используете его для фильтрации ваших данных (столбец фильтра - первый), а затем упорядочиваете свои данные (столбец заказа - второй).

Если вы удалите фильтр, все будет иначе.Тогда вам нужно просто указать (date, uid) в качестве индекса.

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

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

EDIT

Не то, чтобы мой предложенный индекс для pm_replies был одним индексом, охватывающим три поля, а не только тремя индексами.Это гарантирует, что все записи в индексе предварительно отсортированы по этим столбцам.Это как сортировка данных в Excel по трем столбцам.

Наличие трех отдельных индексов похоже на размещение данных Excel на трех вкладках.Каждое из них отсортировано по разным полям.

Вы получаете такое поведение только с одним индексом на три поля ...
- Вы можете выбрать один «набор» записей с одинаковым msg_id
- Этовся «связка» находится рядом друг с другом, без пробелов и т. д.
- вся эта «связка» отсортирована в порядке дат для этого msg_id
- для любых строк с одинаковой датой они упорядочены по user_id

(Опять же, user_id часть действительно очень мала.)

0 голосов
/ 16 марта 2012

Похоже, оптимизатор пытается принудительно индексировать по идентификатору, чтобы соединить пользовательскую таблицу.Так как вы выполняете левое соединение (что не имеет смысла, так как я ожидаю, что каждая запись будет иметь идентификатор пользователя, то есть обычный INNER JOIN), я оставлю это в левом соединении.

Итак,Я бы попробовал следующее.Запросите только ответы на основе идентификатора MESSAGE и упорядочите их по дате, которая убывает по существу, ПОТОМУ оставленное соединение, например

SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        ( select R2.* 
             from pm_replies R2
             where r2.msg_id = '784351921943772258' ) r
        LEFT JOIN users as u
            ON u.uid = r.uid
    ORDER BY
        r.date DESC

Кроме того, поскольку у меня нет готового MySQL и могуне помните, если в подзапросе разрешен упорядочение по, если это так, вы можете оптимизировать внутренний предварительный запрос (используя псевдоним "R2") и разместить там порядок, поэтому он использует индекс (msgid, date) и возвращает толькоэтот набор ... ТО присоединяется к пользовательской таблице с идентификатором, для которого в этой точке не требуется индекс из результирующего набора SOURCE, а просто индекс по пользовательской таблице, чтобы найти совпадение.

0 голосов
/ 16 марта 2012

То, что говорит Демс, должно быть правильным, но есть одна дополнительная деталь, если вы используете InnoDB: возможно, вы платите цену вторичных индексов для кластеризованных таблиц - по сути, Для доступа к строке через вторичный индекс требуется дополнительный поиск через первичный, то есть кластеризованный индекс. Такой «двойной поиск» может сделать индекс менее привлекательным для оптимизатора запросов.

Чтобы облегчить это, попробуйте , охватывающий все поля в вашем операторе select с индексом:

pm_replies: (msg_id, date, uid, reply_id, body, date)
users:      (uid, username, profile_picture)
0 голосов
/ 16 марта 2012

Добавьте дату к ключу index1, чтобы msg_id и date были в индексе.

0 голосов
/ 16 марта 2012

Пожалуйста, попробуйте это:

SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        pm_replies as r
        LEFT JOIN users as u
            ON (u.uid = r.uid AND r.msg_id = '784351921943772258')
    ORDER BY r.date DESC

в моем случае это поможет.

...