如何提高查询性能(使用解释命令结果f.e.)
How to improve query performance (using explain command results f.e.)
我目前正在运行进行此查询。但是,当 运行 在 phpMyAdmin 外部时,它会导致 504 超时错误。我认为这与查询返回或访问行数的效率有关。
我对 MySQL 不是很有经验,所以这是我能做的最好的:
SELECT
s.surveyId,
q.cat,
SUM((sac.answer_id*q.weight))/SUM(q.weight) AS score,
user.division_id,
user.unit_id,
user.department_id,
user.team_id,
division.division_name,
unit.unit_name,
dpt.department_name,
team.team_name
FROM survey_answers_cache sac
JOIN surveys s ON s.surveyId = sac.surveyid
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
JOIN user ON user.user_id = sac.user_id
JOIN questions q ON q.question_id = sac.question_id
JOIN division ON division.division_id = user.division_id
LEFT JOIN unit ON unit.unit_id = user.unit_id
LEFT JOIN department dpt ON dpt.department_id = user.department_id
LEFT JOIN team ON team.team_id = user.team_id
WHERE c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
GROUP BY user.team_id, s.surveyId, q.cat
ORDER BY s.surveyId, user.team_id, q.cat ASC
我在这个查询中遇到的问题是,当我得到一个正确的结果时,返回它的速度很快 运行 秒(比方说 +-500 毫秒),但是当结果的行数是原来的两倍时,它需要的时间超过5 分钟,然后导致 504 超时。
另一个问题是我没有自己创建这个数据库,所以我没有自己设置索引。我正在考虑改进这些,因此我使用了解释命令:
我看到很多主键和几个双索引,但我不确定这是否会对性能产生如此大的影响。
编辑:这段代码占用了所有的执行时间:
$start_time = microtime(true);
$stmt = $conn->query($query); //query is simply the query above.
while ($row = $stmt->fetch_assoc()){
$resultSurveys["scores"][] = $row;
}
$stmt->close();
$end_time = microtime(true);
$duration = $end_time - $start_time; //value typically the execution time #reallyHigh...
所以我的问题:是否可以(极大地?)通过更改数据库键来提高查询性能,或者应该我将查询分成多个较小的查询?
我认为当您添加以下内容时会出现问题:
JOIN user ON user.cluster_id = sc.subcluster_id
JOIN survey_answers_cache sac ON (sac.surveyId = s.surveyId AND sac.user_id = user.user_id)
附加条件sac.user_id = user.user_id很容易不一致
您可以尝试与用户 table 进行第二次加入吗?
PD。你能加一个"SHOW CREATE TABLE"
吗
EXPLAIN 结果显示出问题迹象
使用临时文件;使用文件排序:ORDER BY 需要创建临时文件 table 来进行排序。
用户table第3行type
为ALL,key
和ref
为NULL:表示需要扫描整个table每次检索结果。
建议:
- 在user.cluster_id和ORDER BY和GROUP by子句中涉及的所有字段上添加索引。请记住,用户 table 似乎在
changein
数据库下(跨数据库查询)。
- 在 JOIN 涉及的用户列上添加索引。
- 将索引添加到 s.survey_id
- 如果可能,GROUP BY 和 ORDER BY 子句保持相同的顺序
- 根据 accepted answer in this question 将用户 table 上的 JOIN 移动到加入队列中的第一个位置。
- 仔细阅读这篇文章official documentation。您可能需要优化服务器配置。
PS:查询优化是一门需要耐心和努力的艺术。没有灵丹妙药。
欢迎使用优化艺术MySQL!
你可以试试这个(虽然我测试这个不切实际)
SELECT
sac.surveyId,
q.cat,
SUM((sac.answer_id*q.weight))/SUM(q.weight) AS score,
user.division_id,
user.unit_id,
user.department_id,
user.team_id,
division.division_name,
unit.unit_name,
dpt.department_name,
team.team_name
FROM survey_answers_cache sac
JOIN
(
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
) AS v ON v.surveyid = sac.surveyid
JOIN user ON user.user_id = sac.user_id
JOIN questions q ON q.question_id = sac.question_id
JOIN division ON division.division_id = user.division_id
LEFT JOIN unit ON unit.unit_id = user.unit_id
LEFT JOIN department dpt ON dpt.department_id = user.department_id
LEFT JOIN team ON team.team_id = user.team_id
GROUP BY user.team_id, v.surveyId, q.cat
ORDER BY v.surveyId, user.team_id, q.cat ASC
所以我希望我没有搞砸任何事情。
无论如何,想法是在内部查询中,您 select 仅根据您的 where 条件查询您需要的行。这将创建一个更小的 tmp table,因为它只提取 2 个字段都是整数。
然后在外部查询中加入 tables,您实际上从中提取其余数据、排序和分组。这样您就可以在较小的数据集上进行排序和分组。而你的 where 子句可以 运行 以最优化的方式。
您甚至可以省略其中一些 table,因为您只是从其中的一些中提取数据,但如果没有看到完整的模式以及它之间的关系,这很难说。
不过一般来说这部分(子查询)
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
是什么直接受你的 WHERE 子句影响。看看我们可以优化这部分,然后用它来加入您需要的其余数据。
一个删除tables的例子很容易从上面推导出来,考虑这个
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
WHERE
sc.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
c
table cluster
从不用于提取数据,仅用于 where。所以不是
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=?
与
相同或等同
WHERE
sc.cluster_id=?
因此我们可以完全消除该连接。
我目前正在运行进行此查询。但是,当 运行 在 phpMyAdmin 外部时,它会导致 504 超时错误。我认为这与查询返回或访问行数的效率有关。
我对 MySQL 不是很有经验,所以这是我能做的最好的:
SELECT
s.surveyId,
q.cat,
SUM((sac.answer_id*q.weight))/SUM(q.weight) AS score,
user.division_id,
user.unit_id,
user.department_id,
user.team_id,
division.division_name,
unit.unit_name,
dpt.department_name,
team.team_name
FROM survey_answers_cache sac
JOIN surveys s ON s.surveyId = sac.surveyid
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
JOIN user ON user.user_id = sac.user_id
JOIN questions q ON q.question_id = sac.question_id
JOIN division ON division.division_id = user.division_id
LEFT JOIN unit ON unit.unit_id = user.unit_id
LEFT JOIN department dpt ON dpt.department_id = user.department_id
LEFT JOIN team ON team.team_id = user.team_id
WHERE c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
GROUP BY user.team_id, s.surveyId, q.cat
ORDER BY s.surveyId, user.team_id, q.cat ASC
我在这个查询中遇到的问题是,当我得到一个正确的结果时,返回它的速度很快 运行 秒(比方说 +-500 毫秒),但是当结果的行数是原来的两倍时,它需要的时间超过5 分钟,然后导致 504 超时。 另一个问题是我没有自己创建这个数据库,所以我没有自己设置索引。我正在考虑改进这些,因此我使用了解释命令:
我看到很多主键和几个双索引,但我不确定这是否会对性能产生如此大的影响。
编辑:这段代码占用了所有的执行时间:
$start_time = microtime(true);
$stmt = $conn->query($query); //query is simply the query above.
while ($row = $stmt->fetch_assoc()){
$resultSurveys["scores"][] = $row;
}
$stmt->close();
$end_time = microtime(true);
$duration = $end_time - $start_time; //value typically the execution time #reallyHigh...
所以我的问题:是否可以(极大地?)通过更改数据库键来提高查询性能,或者应该我将查询分成多个较小的查询?
我认为当您添加以下内容时会出现问题:
JOIN user ON user.cluster_id = sc.subcluster_id
JOIN survey_answers_cache sac ON (sac.surveyId = s.surveyId AND sac.user_id = user.user_id)
附加条件sac.user_id = user.user_id很容易不一致
您可以尝试与用户 table 进行第二次加入吗?
PD。你能加一个"SHOW CREATE TABLE"
吗EXPLAIN 结果显示出问题迹象
使用临时文件;使用文件排序:ORDER BY 需要创建临时文件 table 来进行排序。
用户table第3行type
为ALL,key
和ref
为NULL:表示需要扫描整个table每次检索结果。
建议:
- 在user.cluster_id和ORDER BY和GROUP by子句中涉及的所有字段上添加索引。请记住,用户 table 似乎在
changein
数据库下(跨数据库查询)。 - 在 JOIN 涉及的用户列上添加索引。
- 将索引添加到 s.survey_id
- 如果可能,GROUP BY 和 ORDER BY 子句保持相同的顺序
- 根据 accepted answer in this question 将用户 table 上的 JOIN 移动到加入队列中的第一个位置。
- 仔细阅读这篇文章official documentation。您可能需要优化服务器配置。
PS:查询优化是一门需要耐心和努力的艺术。没有灵丹妙药。 欢迎使用优化艺术MySQL!
你可以试试这个(虽然我测试这个不切实际)
SELECT
sac.surveyId,
q.cat,
SUM((sac.answer_id*q.weight))/SUM(q.weight) AS score,
user.division_id,
user.unit_id,
user.department_id,
user.team_id,
division.division_name,
unit.unit_name,
dpt.department_name,
team.team_name
FROM survey_answers_cache sac
JOIN
(
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
) AS v ON v.surveyid = sac.surveyid
JOIN user ON user.user_id = sac.user_id
JOIN questions q ON q.question_id = sac.question_id
JOIN division ON division.division_id = user.division_id
LEFT JOIN unit ON unit.unit_id = user.unit_id
LEFT JOIN department dpt ON dpt.department_id = user.department_id
LEFT JOIN team ON team.team_id = user.team_id
GROUP BY user.team_id, v.surveyId, q.cat
ORDER BY v.surveyId, user.team_id, q.cat ASC
所以我希望我没有搞砸任何事情。
无论如何,想法是在内部查询中,您 select 仅根据您的 where 条件查询您需要的行。这将创建一个更小的 tmp table,因为它只提取 2 个字段都是整数。
然后在外部查询中加入 tables,您实际上从中提取其余数据、排序和分组。这样您就可以在较小的数据集上进行排序和分组。而你的 where 子句可以 运行 以最优化的方式。
您甚至可以省略其中一些 table,因为您只是从其中的一些中提取数据,但如果没有看到完整的模式以及它之间的关系,这很难说。
不过一般来说这部分(子查询)
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
是什么直接受你的 WHERE 子句影响。看看我们可以优化这部分,然后用它来加入您需要的其余数据。
一个删除tables的例子很容易从上面推导出来,考虑这个
SELECT
s.surveyId,
sc.subcluster_id
FROM
surveys s
JOIN subcluster sc ON s.subcluster_id = sc.subcluster_id
WHERE
sc.cluster_id=? AND sc.subcluster_id=? AND s.active=0 AND s.prepare=0
c
table cluster
从不用于提取数据,仅用于 where。所以不是
JOIN cluster c ON sc.cluster_id = c.cluster_id
WHERE
c.cluster_id=?
与
相同或等同WHERE
sc.cluster_id=?
因此我们可以完全消除该连接。