首先,我看了几个关于优化SQL查询的问题,但我仍然不清楚我的情况是什么导致了我的问题。我也阅读了一些关于这个主题的文章,并试图实现几个可能的解决方案,正如我将在下面描述的那样,但是还没有任何工作,甚至没有对这个问题产生明显的影响。
该应用程序是一个营养跟踪系统 - 用户输入他们吃的食物,并根据进口的USDA数据库,应用程序将食物分解为单独的营养素,并为用户提供营养素数量的细分(现在)每日。
这里的 A PDF of the abbreviated database schema 和here它是一个(可能质量很差)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%左右,但不是我正在寻找的数量级)。
我很乐意提供任何建议,并乐意提供您所需的更多信息,以帮助诊断和改善此问题。提前谢谢!
答案 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;