0

私は決して DBA ではありませんが、単純なデータベースについては独学できると思っていました。ただし、このクエリには困惑しています。

私は2つのテーブルを持っています。1 つは、私の Web サイトの各ページの名前を保持します。もう 1 つは、ページがアクセスされるたびに 1 つのレコードを保持します。これら 2 つのテーブルを結合して、各ページの実行時間を平均および合計所要時間として調べ、最適化の取り組みが必要な場所を判断できるようにします。これは私のクエリです:

 SELECT reqtemp.template_path, reqtemp.template_name,
        round(AVG(req.request_execution_time)) AS week_avg_exec_time, COUNT(DISTINCT req.skey) AS week_count, SUM(req.request_execution_time) AS week_total_time,
        round(AVG(req2.request_execution_time)) AS month_avg_exec_time, COUNT(DISTINCT req2.skey) AS month_count, SUM(req2.request_execution_time) AS month_total_time,
        round(AVG(req3.request_execution_time)) AS old_avg_exec_time, COUNT(DISTINCT req3.skey) AS old_count, SUM(req3.request_execution_time) AS old_total_time
 FROM   log_exectime_request_tmp reqtemp
        LEFT JOIN log_exectime_request req ON reqtemp.skey = req.log_exectime_req_tmp_skey
                                          AND req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
        LEFT JOIN log_exectime_request req2 ON reqtemp.skey = req2.log_exectime_req_tmp_skey
                                          AND req2.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
        LEFT JOIN log_exectime_request req3 ON reqtemp.skey = req3.log_exectime_req_tmp_skey
                                          AND req3.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY)
                                          AND req3.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
 GROUP by reqtemp.template_path, reqtemp.template_name
 ORDER BY week_total_time DESC

私の理解では、これは大きなテーブルに 2000 レコードしかないミリ秒単位で実行されるはずです。少なくとも、最終的には数十万または数百万のレコードを本番環境で実行する必要があるため、期待しています。代わりに、前回試したときは 53 分かかりました。「Explain」は、log_exectime_request_tmp でテーブル スキャンを実行していることを示しています。これは、テーブルに 83 レコードしかなく、その両方の列が使用されているため問題ありません。他の 3 つの結合は、skey と request_date のインデックスを使用していますが、これも正しいようです。

誰でも最適化のアドバイスを提供できますか? なぜこのような小さなテーブルがこのような問題を引き起こすのでしょうか? クエリの構成が不十分ですか?

テーブルの DDL は次のとおりですが、ファイルを添付してデータを入力する方法が見つかりません (見逃しているのでしょうか?)。

 CREATE TABLE `log_exectime_request` 
 (
    `skey` integer (11) NOT NULL AUTO_INCREMENT , 
    `log_exectime_req_tmp_skey` integer (11), 
    `request_date` datetime, 
    `request_execution_time` integer (11), 
    `query_execution_time` integer (11), 
    `template_execution_time` integer (11), 
    `logging_execution_time` integer (11),
    PRIMARY KEY (`skey`)
 ) TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_swedish_ci;

 ALTER TABLE `elegantgalleries`.`log_exectime_request` ADD INDEX `date_tempSkey` (`log_exectime_req_tmp_skey`,`request_date` );


 CREATE TABLE `log_exectime_request_tmp` 
 (
    `skey` integer (11) NOT NULL AUTO_INCREMENT , 
    `template_path` varchar (500), 
    `template_name` varchar (250),
    PRIMARY KEY (`skey`)
 ) TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_swedish_ci;
4

2 に答える 2

2

このような質問をするときは、使用しているデータベースについて常に言及する必要があります。データベースが異なれば、パフォーマンス特性も大きく異なります。

クエリを正しく理解していれば、それほど多くの結合を行う必要がないため、非常に時間がかかります。3つの異なるグループでリクエストを合計しようとしているようです。。。前の週、前の月、および1年前(ただし、365日から395日ではなく、335日から365日を使用しているのは不思議です)。

必要なクエリは次のようになります。

SELECT reqtemp.template_path, reqtemp.template_name,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                      then req.request_execution_time
                 end)
            ) as week_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                           then req.skey
             end) AS week_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                then req.request_execution_time
          ) AS month_total_time,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                      then req.request_execution_time
                 end)
            ) as month_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                           then req.skey
             end) AS week_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                then req.request_execution_time
          ) AS month_total_time,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                           req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                      then req.request_execution_time
                 end)
            ) as old_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                                req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                           then req.skey
             end) AS old_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                     req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                then req.request_execution_time
          ) AS old_total_time
FROM log_exectime_request_tmp reqtemp LEFT JOIN
     log_exectime_request req
     ON reqtemp.skey = req.log_exectime_req_tmp_skey
GROUP by reqtemp.template_path, reqtemp.template_name
ORDER BY week_total_time DESC

リクエストの日付を取得するには、reqテーブルに1回だけ参加する必要があります。次に、caseステートメントを使用して、時間を異なる期間に分割できます。

元のクエリは、各期間のすべてのレコード間でクロス結合を実行しています。与えられた期間には数百または数千しかないかもしれませんが、それらをクロス結合すると、数百万または数十億の行が得られます。

于 2012-07-08T15:08:24.100 に答える
1

request_dateでフィルタリングしているので、その列だけでインデックスを追加してみてください。

于 2012-07-08T14:45:07.413 に答える