如何提高查询性能(使用解释命令结果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,keyref为NULL:表示需要扫描整个table每次检索结果。

建议:

  1. 在user.cluster_id和ORDER BY和GROUP by子句中涉及的所有字段上添加索引。请记住,用户 table 似乎在 changein 数据库下(跨数据库查询)。
  2. 在 JOIN 涉及的用户列上添加索引。
  3. 将索引添加到 s.survey_id
  4. 如果可能,GROUP BY 和 ORDER BY 子句保持相同的顺序
  5. 根据 accepted answer in this question 将用户 table 上的 JOIN 移动到加入队列中的第一个位置。
  6. 仔细阅读这篇文章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=?

因此我们可以完全消除该连接。