0

アプリケーションに単純な SQL ステートメントがあります。

  SELECT SQL_NO_CACHE key_event_id, MAX(report_ts) AS max_ts
  FROM `key_event_reports`
  WHERE report_model_id = 2 
  GROUP BY key_event_id;

key_event_reportsテーブルは中サイズ (~ 17M 行) で、テーブルの定義は次のとおりです 。

CREATE TABLE IF NOT EXISTS `key_event_reports` (
  `key_event_report_id` int(20) NOT NULL AUTO_INCREMENT,
  `report_model_id` int(5) NOT NULL,
  `key_event_id` int(5) NOT NULL,
  `title_id` int(15) NOT NULL,
  `report_ts` datetime NOT NULL,
  `report_time` time NOT NULL,
  `total` int(7) NOT NULL DEFAULT '0',
  `pos` int(7) NOT NULL DEFAULT '0',
  `neg` int(7) NOT NULL DEFAULT '0',
  `smooth_total` float NOT NULL DEFAULT '0',
  `smooth_pos` float NOT NULL DEFAULT '0',
  `smooth_neg` float NOT NULL DEFAULT '0',
  `buzz` float NOT NULL DEFAULT '0',
  `sentiment` float NOT NULL DEFAULT '0',
  PRIMARY KEY (`key_event_report_id`),
  UNIQUE KEY `key_event_id_4` (`key_event_id`,`report_model_id`,`title_id`,`report_ts`),
  KEY `report_model_id` (`key_event_id`,`report_time`),
  KEY `report_model_id_2` (`report_model_id`,`key_event_id`,`report_ts`),
  KEY `key_event_id` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_total`),
  KEY `key_event_id_3` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_pos`),
  KEY `key_event_id_2` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_neg`),
  KEY `get_latest_report` (`report_model_id`,`report_ts`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=16967636 ;

report_model_id は常に 2 で (データベースにはまだ他のモデルはありませんが、これはすぐに変更される可能性があります)、10 分ごとに報告される 10 の異なる key_events があります。

このクエリは、キャッシュなしでは非常に時間がかかります (約 20 秒)。上記のクエリがより大きなステートメントのサブクエリとして使用されると、問題はさらに悪化します。

SET @report_model_id = 2;
SET @message_id = ?;
SET @title_id = ?
SET @min_score = 5;

SET @min_message_id = ( 
    SELECT MIN(message_id)  
    FROM `messages`  
    WHERE msg_time > DATE_SUB(NOW(), INTERVAL 20 MINUTE) 
); 

SELECT 
    ke.key_event_id AS key_event_id, 
    COALESCE(kermmid.message_id, MIN(mhke.message_id)) AS max_message_id, 
    ker_max.max_ts AS last_report_ts 
FROM `key_events` ke
LEFT JOIN (
    SELECT key_event_id, MAX(report_ts) AS max_ts
    FROM `key_event_reports`
    WHERE report_model_id = 2 
    GROUP BY key_event_id
) ker_max
    ON ( ker_max.key_event_id = ke.key_event_id )
    LEFT JOIN `key_event_reports` ker 
        ON (
            ker.key_event_id = ke.key_event_id 
            AND ker.report_model_id = @report_model_id 
            AND ker.title_id = @title_id 
            AND ker.report_ts = @actcurrent 
        ) 
    LEFT JOIN `key_event_report_max_message_ids` kermmid 
        ON (
            kermmid.key_event_id = ker.key_event_id 
            AND kermmid.report_model_id = ker.report_model_id 
            AND kermmid.report_ts = ker.report_ts 
        ) 
    LEFT JOIN `messages_has_key_events` mhke 
        ON ( 
            mhke.key_event_id = ke.key_event_id 
            AND mhke.title_id = @title_id 
            AND mhke.message_id > @min_message_id 
            AND mhke.message_id < @message_id 
            AND mhke.score > @min_score 
        ) 
    GROUP BY 
        ke.key_event_id;

これでサブクエリを使用すると、実行時間も 50 ミリ秒から 20 秒以上になります。

この理由は何ですか?ステートメントまたはDB構造を最適化するにはどうすればよいですか?

4

3 に答える 3

3

インデックス(report_model_id,key_event_id,report_ts)を追加report_model_idして、group by に追加してみてください。これにより、グループ化による最適化を使用できるようになります。

SELECT key_event_id, MAX(report_ts) AS max_ts
FROM `key_event_reports`
WHERE report_model_id = 2 
GROUP BY report_model_id, key_event_id

私はまだクエリの残りの方法を見つけようとしています...内部の SELECT は LEFT JOIN である必要がありますか、それとも INNER JOIN でよいでしょうか?

編集:すでにインデックスを持っているという事実を見逃したので、フィールドをGROUP BYに追加するだけです。

于 2013-06-18T16:18:23.437 に答える
1

あなたのクエリはすでにこのインデックスを使用しているようです。

`report_model_id_2` (`report_model_id`,`key_event_id`,`report_ts`)

これにはクエリが必要とするすべての情報が含まれているため、MySQL はテーブル全体ではなくこのインデックスに対して範囲スキャンを実行することで、クエリを満たすことができます。良いニュースは、クエリがかなり適切に最適化されたことです。それも悪いニュースです。

サマリー テーブルを作成し、MySQL データベースにイベントを設定して、詳細データからサマリー テーブルを更新することは理にかなっていますか? これは、このクエリの結果が少し遅れて実行されても、アプリケーションにとって致命的でない場合にのみ当てはまります。

この情報をディテール テーブルと完全に同期させる必要がある場合は、サマリー テーブルを更新するトリガーを設定することもできます。

于 2013-06-18T16:20:27.203 に答える