Оптимизация производительности MySQL: поле по дате и времени - PullRequest
36 голосов
/ 03 апреля 2009

У меня есть таблица с примерно 100 000 публикаций в блоге, связанная с таблицей из 50 каналов через отношение 1: n. Когда я запрашиваю обе таблицы с помощью оператора select, упорядоченного по полю datetime в таблице публикаций, MySQL всегда использует файловую сортировку, что приводит к очень медленному времени запроса (> 1 секунда). Вот схема таблицы postings (упрощенная):

+---------------------+--------------+------+-----+---------+----------------+
| Field               | Type         | Null | Key | Default | Extra          |
+---------------------+--------------+------+-----+---------+----------------+
| id                  | int(11)      | NO   | PRI | NULL    | auto_increment |
| feed_id             | int(11)      | NO   | MUL | NULL    |                |
| crawl_date          | datetime     | NO   |     | NULL    |                |
| is_active           | tinyint(1)   | NO   | MUL | 0       |                |
| link                | varchar(255) | NO   | MUL | NULL    |                |
| author              | varchar(255) | NO   |     | NULL    |                |
| title               | varchar(255) | NO   |     | NULL    |                |
| excerpt             | text         | NO   |     | NULL    |                |
| long_excerpt        | text         | NO   |     | NULL    |                |
| user_offtopic_count | int(11)      | NO   | MUL | 0       |                |
+---------------------+--------------+------+-----+---------+----------------+

А вот таблица feed:

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| type        | int(11)      | NO   | MUL | 0       |                |
| title       | varchar(255) | NO   |     | NULL    |                |
| website     | varchar(255) | NO   |     | NULL    |                |
| url         | varchar(255) | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

А вот запрос, выполнение которого занимает> 1 секунды. Обратите внимание, что поле post_date имеет индекс, но MySQL не использует его для сортировки таблицы сообщений:

SELECT 
    `postings`.`id`, 
    UNIX_TIMESTAMP(postings.post_date) as post_date, 
    `postings`.`link`, 
    `postings`.`title`, 
    `postings`.`author`, 
    `postings`.`excerpt`, 
    `postings`.`long_excerpt`, 
    `feeds`.`title` AS feed_title, 
    `feeds`.`website` AS feed_website
FROM 
    (`postings`)
JOIN 
    `feeds` 
ON 
    `feeds`.`id` = `postings`.`feed_id`
WHERE 
    `feeds`.`type` = 1 AND 
    `postings`.`user_offtopic_count` < 10 AND 
    `postings`.`is_active` = 1
ORDER BY 
    `postings`.`post_date` desc
LIMIT 
    15  

Результат команды explain extended в этом запросе показывает, что MySQL использует файловую сортировку:

+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+
| id | select_type | table    | type   | possible_keys                         | key       | key_len | ref                      | rows  | Extra                       |
+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+
|  1 | SIMPLE      | postings | ref    | feed_id,is_active,user_offtopic_count | is_active | 1       | const                    | 30996 | Using where; Using filesort |
|  1 | SIMPLE      | feeds    | eq_ref | PRIMARY,type                          | PRIMARY   | 4       | feedian.postings.feed_id |     1 | Using where                 |
+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+

Когда я удаляю часть order by, MySQL перестает использовать сортировку файлов. Пожалуйста, дайте мне знать, если у вас есть идеи о том, как оптимизировать этот запрос, чтобы MySQL сортировал и отбирал данные с помощью индексов. Я уже попробовал несколько вещей, таких как создание комбинированного индекса для всех полей where / order by, как предлагалось в нескольких публикациях в блогах, но это тоже не сработало.

Ответы [ 3 ]

37 голосов
/ 03 апреля 2009

Создайте составной индекс либо на postings (is_active, post_date) (в таком порядке).

Он будет использоваться как для фильтрации на is_active, так и для заказа на post_date.

MySQL должен показывать REF метод доступа к этому индексу в EXPLAIN EXTENDED.

Обратите внимание, что у вас есть RANGE условие фильтрации для user_offtopic_count, поэтому вы не можете использовать индекс по этому полю как при фильтрации, так и при сортировке по другому полю.

В зависимости от того, насколько избирательным является ваш user_offtopic_count (т. Е. Сколько строк удовлетворяет user_offtopic_count < 10), может быть более полезно создать индекс для user_offtopic_count и позволить отсортировать post_dates.

Для этого создайте составной индекс для postings (is_active, user_offtopic_count) и убедитесь, что для этого индекса используется метод доступа RANGE.

Какой индекс будет быстрее, зависит от вашего распределения данных. Создайте оба индекса, FORCE их и посмотрите, что быстрее:

CREATE INDEX ix_active_offtopic ON postings (is_active, user_offtopic_count);
CREATE INDEX ix_active_date ON postings (is_active, post_date);

SELECT 
    `postings`.`id`, 
    UNIX_TIMESTAMP(postings.post_date) as post_date, 
    `postings`.`link`, 
    `postings`.`title`, 
    `postings`.`author`, 
    `postings`.`excerpt`, 
    `postings`.`long_excerpt`, 
    `feeds`.`title` AS feed_title, 
    `feeds`.`website` AS feed_website
FROM 
    `postings` FORCE INDEX (ix_active_offtopic)
JOIN 
    `feeds` 
ON 
    `feeds`.`id` = `postings`.`feed_id`
WHERE 
    `feeds`.`type` = 1 AND 
    `postings`.`user_offtopic_count` < 10 AND 
    `postings`.`is_active` = 1
ORDER BY 
    `postings`.`post_date` desc
LIMIT 
    15

/* This should show RANGE access with few rows and keep the FILESORT */

SELECT 
    `postings`.`id`, 
    UNIX_TIMESTAMP(postings.post_date) as post_date, 
    `postings`.`link`, 
    `postings`.`title`, 
    `postings`.`author`, 
    `postings`.`excerpt`, 
    `postings`.`long_excerpt`, 
    `feeds`.`title` AS feed_title, 
    `feeds`.`website` AS feed_website
FROM 
    `postings` FORCE INDEX (ix_active_date)
JOIN 
    `feeds` 
ON 
    `feeds`.`id` = `postings`.`feed_id`
WHERE 
    `feeds`.`type` = 1 AND 
    `postings`.`user_offtopic_count` < 10 AND 
    `postings`.`is_active` = 1
ORDER BY 
    `postings`.`post_date` desc
LIMIT 
    15

/* This should show REF access with lots of rows and no FILESORT */
3 голосов
/ 25 августа 2009

Также важно помнить, что MySQL не будет использовать индекс, если к столбцу, по которому вы упорядочиваете, применена функция.

Вам также следует попробовать создать псевдоним postings.post_date как-нибудь еще. Это скажет MySQL упорядочить по неизмененному столбцу, и вы все равно выберете метку времени Unix.

3 голосов
/ 03 апреля 2009

MySQL имеет два алгоритма сортировки файлов: старая сортировка файлов, которая сортирует записи на диске, и новая версия, работающая в памяти.

Если он не может использовать индекс первой таблицы в объединении для сортировки запроса, ему придется выполнить сортировку файлов. Если результирующий набор перед сортировкой, преобразованный в формат фиксированной ширины, больше, чем буфер сортировки ИЛИ , если он содержит какие-либо текстовые поля, ему придется использовать более медленный алгоритм сортировки файлов на диске (второе условие выполняется, так как запрос имеет текстовое поле).

MySQL выбирает использование столбца is_active, якобы потому, что считает этот столбец наиболее избирательным при удалении строк перед продолжением других объединений и условий. Первое, что я бы предложил, это попытаться создать составные индексы с post_date, feed_id и столбцами в условии where, например, (is_active, user_offtopic_count, post_date, feed_id).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...