优化/改进慢速mysql查询 - 索引?重组?

时间:2011-08-03 03:03:20

标签: mysql sql query-optimization

首先,我看了几个关于优化SQL查询的问题,但我仍然不清楚我的情况是什么导致了我的问题。我也阅读了一些关于这个主题的文章,并试图实现几个可能的解决方案,正如我将在下面描述的那样,但是还没有任何工作,甚至没有对这个问题产生明显的影响。

该应用程序是一个营养跟踪系统 - 用户输入他们吃的食物,并根据进口的USDA数据库,应用程序将食物分解为单独的营养素,并为用户提供营养素数量的细分(现在)每日。

这里的 A PDF of the abbreviated database schemahere它是一个(可能质量很差)JPG。我在开放式办公室做了这个 - 如果有更好的方法可视化数据库的建议,我也会对这方面的建议持开放态度!蓝色表格直接来自美国农业部,绿色和黑色表格是我制作的。我省略了很多数据,以免造成不必要的混乱。

这是我正在尝试运行的查询需要很长时间:

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秒,这很慢,但并不完全可怕。另外,每个查询分别需要~.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'函数,因为我知道在查询中有一个函数通常意味着全表扫描,这显然要慢得多。不幸的是,移除'sum'似乎也没有什么区别(好吧,大约3-5%左右,但不是我正在寻找的数量级)。

我很乐意提供任何建议,并乐意提供您所需的更多信息,以帮助诊断和改善此问题。提前谢谢!

2 个答案:

答案 0 :(得分:0)

您的解释中有一些type All建议全表扫描。因此创建临时表。如果它不存在,你可以重新索引。

Sort和Group By通常是性能杀手,你可以调整Mysql内存设置,以避免在有额外内存的情况下对tmp表进行物理i / o。

最后,尝试确保连接属性的数据类型匹配。即data.date_time = listing.date_time具有相同的数据格式。

希望有所帮助。

答案 1 :(得分:0)

好的,所以我最终想出了我最终要做的事情。我无法更快地进行“数据”查询 - 这仍然是瓶颈。但是现在我已经做到了,所以整个查询过程非常接近线性,而不是指数。 我将查询分成两部分,并将每一部分分成一个临时表。然后我为每个临时表添加了一个索引,然后分别进行连接。这使得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;