Оптимизация / улучшение медленного MySQL запроса - индексация? реорганизации? - PullRequest
2 голосов
/ 03 августа 2011

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

Приложение представляет собой систему отслеживания питания - пользователи вводят продукты, которые они едят, и на основе импортированной базы данных USDA приложение разбивает продукты на отдельные питательные вещества и дает пользователю разбивку количеств питательных веществ по (на данный момент) ежедневно.

здесь PDF сокращенной схемы базы данных и здесь это как (возможно, плохое качество) JPG. Я сделал это в открытом офисе - если есть предложения по лучшим способам визуализации базы данных, я также открыт для предложений в этой области! Синие столы прямо из USDA, а зеленые и черные - те, которые я сделал. Я опустил много данных, чтобы не загромождать ненужные вещи.

Вот запрос, который я пытаюсь выполнить, который занимает очень много времени:

SELECT listing.date_time,listing.nutrdesc,data.total_nutr_mass,listing.units 
FROM 
    (SELECT nutrdesc, nutr_no, date_time, units 
    FROM meals, nutr_def 
    WHERE meals.users_userid = '2' 
        AND date_time BETWEEN '2009-8-12' AND '2009-9-12' 
        AND (nutr_no <100000 
            OR nutr_no IN 
            (SELECT nutr_def_nutr_no 
            FROM nutr_rights 
            WHERE nutr_rights.users_userid = '2'))
    ) as listing
LEFT JOIN 
(SELECT nutrdesc, date_time, nut_data.nutr_no, sum(ingred_gram_mass*entry_qty_num*nutr_val/100) AS total_nutr_mass 
FROM nut_data, recipe_ingredients, food_entries, meals, nutr_def 
WHERE nut_data.nutr_no = nutr_def.nutr_no 
    AND ndb_no = ingred_ndb_no 
    AND foods_food_id = entry_ident 
    AND meals_meal_id = meal_id 
    AND users_userid = '2' 
    AND date_time BETWEEN '2009-8-12' AND '2009-9-12' 
GROUP BY date_time,nut_data.nutr_no ) as data 
ON data.date_time = listing.date_time 
AND listing.nutr_no = data.nutr_no 
ORDER BY listing.date_time,listing.nutrdesc,listing.units 

Так что я знаю, что это довольно сложно - первый выбор получает список всех питательных веществ, которые пользователь потребил в указанном диапазоне дат, а второй заполняет все количества.

Когда я реализую их отдельно, первый запрос действительно быстрый, но второй - медленный, а очень медленный, когда диапазоны дат становятся большими. Объединение делает все это смехотворно медленным. Я знаю, что «главная» проблема - это объединение этих двух производных таблиц, и я могу избавиться от этого и выполнить соединение вручную в основном в php гораздо быстрее, но я не уверен, что это вся история.

Например: для данных за 1 месяц запрос занимает около 8 секунд, что медленно, но не совсем ужасно. Отдельно каждый запрос занимает ~ 0,01 и ~ 2 секунды соответственно. 2 секунды все еще кажутся мне высокими.

Если я пытаюсь получить данные за год, выполнение всего запроса занимает несколько (> 10) минут, что проблематично - иногда соединение клиент-сервер прерывается, и в любом случае мы не хотим Я не хочу сидеть там с вращающейся иконкой «пожалуйста, подождите». В основном, я чувствую, что есть проблема, потому что для получения 12-кратной дополнительной информации требуется больше, чем в 12 раз, а для правильной работы требуется менее 12 раз.

Вот «объяснение» для каждого из медленных запросов: (все и только вторая половина).

Всего:

+----+--------------------+--------------------+----------------+-------------------------------+------------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
| id | select_type        | table              | type           | possible_keys                 | key              | key_len | ref                                                                   | rows | Extra                                        |
+----+--------------------+--------------------+----------------+-------------------------------+------------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
|  1 | PRIMARY            | <derived2>         | ALL            | NULL                          | NULL             | NULL    | NULL                                                                  | 5053 | Using temporary; Using filesort              | 
|  1 | PRIMARY            | <derived4>         | ALL            | NULL                          | NULL             | NULL    | NULL                                                                  | 4341 |                                              | 
|  4 | DERIVED            | meals              | range          | PRIMARY,day_ind               | day_ind          | 9       | NULL                                                                  |   30 | Using where; Using temporary; Using filesort | 
|  4 | DERIVED            | food_entries       | ref            | meals_meal_id                 | meals_meal_id    | 5       | nutrition.meals.meal_id                                               |   15 | Using where                                  | 
|  4 | DERIVED            | recipe_ingredients | ref            | foods_food_id,ingred_ndb_no   | foods_food_id    | 4       | nutrition.food_entries.entry_ident                                    |    2 |                                              | 
|  4 | DERIVED            | nutr_def           | ALL            | PRIMARY                       | NULL             | NULL    | NULL                                                                  |  174 |                                              | 
|  4 | DERIVED            | nut_data           | ref            | PRIMARY                       | PRIMARY          | 36      | nutrition.nutr_def.nutr_no,nutrition.recipe_ingredients.ingred_ndb_no |    1 |                                              | 
|  2 | DERIVED            | meals              | range          | day_ind                       | day_ind          | 9       | NULL                                                                  |   30 | Using where                                  | 
|  2 | DERIVED            | nutr_def           | ALL            | PRIMARY                       | NULL             | NULL    | NULL                                                                  |  174 | Using where                                  | 
|  3 | DEPENDENT SUBQUERY | nutr_rights        | index_subquery | users_userid,nutr_def_nutr_no | nutr_def_nutr_no | 19      | func                                                                  |    1 | Using index; Using where                     | 
+----+--------------------+--------------------+----------------+-------------------------------+------------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
10 rows in set (2.82 sec)

Второй кусок (данные):

+----+-------------+--------------------+-------+-----------------------------+---------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
| id | select_type | table              | type  | possible_keys               | key           | key_len | ref                                                                   | rows | Extra                                        |
+----+-------------+--------------------+-------+-----------------------------+---------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | meals              | range | PRIMARY,day_ind             | day_ind       | 9       | NULL                                                                  |   30 | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | food_entries       | ref   | meals_meal_id               | meals_meal_id | 5       | nutrition.meals.meal_id                                               |   15 | Using where                                  | 
|  1 | SIMPLE      | recipe_ingredients | ref   | foods_food_id,ingred_ndb_no | foods_food_id | 4       | nutrition.food_entries.entry_ident                                    |    2 |                                              | 
|  1 | SIMPLE      | nutr_def           | ALL   | PRIMARY                     | NULL          | NULL    | NULL                                                                  |  174 |                                              | 
|  1 | SIMPLE      | nut_data           | ref   | PRIMARY                     | PRIMARY       | 36      | nutrition.nutr_def.nutr_no,nutrition.recipe_ingredients.ingred_ndb_no |    1 |                                              | 
+----+-------------+--------------------+-------+-----------------------------+---------------+---------+-----------------------------------------------------------------------+------+----------------------------------------------+
5 rows in set (0.00 sec)

Я «проанализировал» все таблицы, включенные в запрос, и добавил индекс в поле даты и времени, которое объединяет записи о блюдах и продуктах. Я назвал это «day_ind». Я надеялся, что это ускорит ситуацию, но, похоже, это ничего не изменит. Я также попытался удалить функцию 'sum', так как понимаю, что наличие функции в запросе часто будет означать полное сканирование таблицы, которое, очевидно, намного медленнее. К сожалению, удаление «суммы», похоже, тоже не имело значения (ну, примерно 3-5% или около того, но не величина порядка, который я ищу).

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

Ответы [ 2 ]

0 голосов
/ 07 августа 2011

Хорошо, так что я в конце концов понял, что я собираюсь в конечном итоге делать. Я не мог сделать запрос данных быстрее - это все еще узкое место. Но теперь я сделал это так, что общий процесс запроса довольно близок к линейному, а не экспоненциальному. Я разделил запрос на две части и превратил каждую во временную таблицу. Затем я добавил индекс для каждой из этих временных таблиц и впоследствии сделал соединение отдельно. Таким образом, общее время выполнения за 1 месяц падения данных с 8 до 2 секунд, а за 1 год данных с ~ 10 минут до ~ 30 секунд. Думаю, пока достаточно. Я могу работать с этим.

Спасибо за предложения. Вот что я в итоге сделал:

create table listing (
    SELECT nutrdesc, nutr_no, date_time, units 
    FROM meals, nutr_def 
    WHERE meals.users_userid = '2' 
        AND date_time BETWEEN '2009-8-12' AND '2009-9-12' 
        AND (
            nutr_no <100000 OR nutr_no IN (
                SELECT nutr_def_nutr_no 
                FROM nutr_rights 
                WHERE nutr_rights.users_userid = '2'
                )
            )
    );

create table data (
    SELECT nutrdesc, date_time, nut_data.nutr_no, sum(ingred_gram_mass*entry_qty_num*nutr_val/100) AS total_nutr_mass 
    FROM nut_data, recipe_ingredients, food_entries, meals, nutr_def 
    WHERE nut_data.nutr_no = nutr_def.nutr_no 
        AND ndb_no = ingred_ndb_no 
        AND foods_food_id = entry_ident 
        AND meals_meal_id = meal_id 
        AND users_userid = '2' 
        AND date_time BETWEEN '2009-8-12' AND '2009-9-12' 
    GROUP BY date_time,nut_data.nutr_no
);

create index joiner on data(nutr_no, date_time);
create index joiner on listing(nutr_no, date_time);

SELECT listing.date_time,listing.nutrdesc,data.total_nutr_mass,listing.units
FROM listing
LEFT JOIN data
ON data.date_time = listing.date_time
AND listing.nutr_no = data.nutr_no
ORDER BY listing.date_time,listing.nutrdesc,listing.units;
0 голосов
/ 04 августа 2011

В вашем объяснении есть несколько type All, предлагающих полное сканирование таблицы.и, следовательно, создать временную таблицу.Вы можете выполнить повторную индексацию, если ее там еще нет.

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

Наконец, убедитесь, что тип данных атрибутов соединения совпадает.Т.е. data.date_time = list.date_time имеет тот же формат данных.

Надеюсь, это поможет.

...