如何提高查询性能(使用explain命令结果f.e.)

时间:2018-01-25 23:13:27

标签: php mysql performance http-status-code-504

所以我目前正在运行此查询。但是,在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

我得到的问题是,当我得到正确的结果返回时,它会快速运行(假设为+ -500ms)但是当结果有两倍的行时,则需要超过5分钟然后导致504超时。 另一个问题是我自己没有创建这个数据库,所以我没有自己设置索引。我正在考虑改进这些,因此我使用了explain命令:

Explain command result

我看到很多主键和几个双索引,但我不确定这是否会对性能产生很大影响。

编辑:这段代码占用了所有执行时间:

$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...

所以我的问题 :是否可以(非常?)通过更改数据库密钥来提高查询性能,还是应该将查询分成多个较小的查询?

非常感谢任何帮助!

P.S。如果您认为我的问题遗漏了,请不要只是下载,而是在下面写下评论或建议编辑,我会尽力添加所需的信息!

3 个答案:

答案 0 :(得分:1)

EXPLAIN结果显示出问题迹象

使用临时;使用filesort:ORDER BY需要创建临时表来进行排序。

用户表if的第3行是ALL,typekey为NULL:表示每次都需要扫描整个表以检索结果。

建议:

  1. 在user.cluster_id上添加索引以及ORDER BY和GROUP by子句中涉及的所有字段。请记住,用户表似乎位于ref数据库(跨数据库查询)。
  2. 在JOIN上涉及的用户列上添加索引。
  3. 将索引添加到s.survey_id
  4. 如果可能,请为GROUP BY和ORDER BY子句保留相同的序列
  5. 根据accepted answer in this question将用户表上的JOIN移动到连接队列中的第一个位置。
  6. 仔细阅读official documentation。您可能需要优化服务器配置。
  7. PS:查询优化是一门需要耐心和努力的艺术。没有银弹。 欢迎来到优化MySQL的精美艺术!

答案 1 :(得分:1)

你可以尝试这样的事情(虽然我不适合测试这个)

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

所以我希望我没有弄乱任何东西。

无论如何,这个想法是在内部查询中根据你的where条件只选择你需要的行。这将创建一个较小的tmp表,因为它只会提取2个字段。

然后在外部查询中,您将加入到实际从其中拉出其余数据的表,订单和组。这样,您就可以对较小的数据集进行排序和分组。而且你的where子句可以以最佳方式运行。

您甚至可以省略其中一些表作为您从其中一些表中提取的唯一数据,但却没有看到完整的架构以及它的相关性,这很难说。

但一般来说这部分(子查询)

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子句直接影响的是什么。看到这一点,我们可以优化这个部分,然后用它来加入你需要的其他数据。

从上面可以很容易地推断出删除表的示例,请考虑这个

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

ccluster从不用于从中提取数据,仅用于从哪里提取数据。所以不是

    JOIN cluster c ON sc.cluster_id = c.cluster_id
 WHERE
    c.cluster_id=?

相同或相当于

WHERE
    sc.cluster_id=?

因此我们可以彻底消除这种联接。

答案 2 :(得分:0)

我认为当您添加此问题时会发生问题:

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可能很容易不一致。

您可以尝试使用用户表进行第二次加入吗?

概率pd。你可以添加一个" SHOW CREATE TABLE"