2016-10-28 16 views
0

どこに近い値に基づいて劇的に異なる実行時間を提供する制限があるクエリがあります。私はMySQL 5.1を使用しており、私の最大のテーブル「アラート」のレコード数は約100,000,000です。MySQLクエリ実行計画(制限が異なる場合)

私はこの実行計画を見ると、私は幸せです、と私のクエリは秒未満を取る:

mysql> EXPLAIN SELECT alert.id, category.severity, category.classification, category.completion, alert.description, 
->  alert.detectTime, analyzer.name, analyzer.manufacturer, analyzer.model, analyzer.version, 
->  analyzer.class, analyzer.osType, analyzer.osVersion, analyzerNode.hostName, 
->  analyzerNode.address, sourceNode.hostName, sourceNode.address, targetNode.hostName, 
->  targetNode.address, sourceUser.userName, sourceUser.uid, targetUser.userName, targetUser.uid, 
->  alert.filePath, alert.sourceProcessPid, alert.sourceProcessPath, alert.targetProcessPid, 
->  alert.targetProcessPath, alert.acknowledgeUserName, alert.acknowledgeTime, reference.url, reference.meaning 
-> FROM alerts AS alert 
->  LEFT JOIN categories AS category ON category.id = alert.categoryId 
->  LEFT JOIN analyzers AS analyzer ON analyzer.id = alert.analyzerId 
->  LEFT JOIN nodes AS analyzerNode ON analyzerNode.id = alert.analyzerNodeId 
->  LEFT JOIN nodes AS sourceNode ON sourceNode.id = alert.sourceNodeId 
->  LEFT JOIN nodes AS targetNode ON targetNode.id = alert.targetNodeId 
->  LEFT JOIN users AS sourceUser ON sourceUser.id = alert.sourceUserId 
->  LEFT JOIN users AS targetUser ON targetUser.id = alert.targetUserId 
->  LEFT JOIN `references` AS reference ON reference.id = alert.referenceId 
-> WHERE category.severity = 'info' 
-> ORDER BY alert.id DESC 
-> LIMIT 40 OFFSET 0; 
  • | id | select_type |テーブル|タイプ| possible_keys |キー| key_len | ref |行|余分な|
  • | 1 |シンプル|アラート|インデックス| categoryIndex | PRIMARY | 8 | NULL | 40 | |
  • | 1 |シンプル|カテゴリ| eq_ref | PRIMARY、severityIndex | PRIMARY | 8 | am.alert.categoryId | 1 |どこで使う|
  • | 1 |シンプル|アナライザ| eq_ref | PRIMARY | PRIMARY | 8 | am.alert.analyzerId | 1 | |
  • | 1 |シンプル| analyzerNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.analyzerNodeId | 1 | |
  • | 1 |シンプル| sourceNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.sourceNodeId | 1 | |
  • | 1 |シンプル| targetNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.targetNodeId | 1 | |
  • | 1 |シンプル| sourceUser | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.sourceUserId | 1 | |
  • | 1 |シンプル| targetUser | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.targetUserId | 1 | |
  • | 1 |シンプル|リファレンス| eq_ref | PRIMARY | PRIMARY | 8 | am.alert.referenceId | 1 | |

ただし、where句の値を変更すると(この場合は列挙型ですが、それは関係ないと思います)、劇的に異なる結果が得られ、クエリには永遠にかかることがあります。

mysql> EXPLAIN SELECT alert.id, category.severity, category.classification, category.completion, alert.description, 
->  alert.detectTime, analyzer.name, analyzer.manufacturer, analyzer.model, analyzer.version, 
->  analyzer.class, analyzer.osType, analyzer.osVersion, analyzerNode.hostName, 
->  analyzerNode.address, sourceNode.hostName, sourceNode.address, targetNode.hostName, 
->  targetNode.address, sourceUser.userName, sourceUser.uid, targetUser.userName, targetUser.uid, 
->  alert.filePath, alert.sourceProcessPid, alert.sourceProcessPath, alert.targetProcessPid, 
->  alert.targetProcessPath, alert.acknowledgeUserName, alert.acknowledgeTime, reference.url, reference.meaning 
-> FROM alerts AS alert 
->  LEFT JOIN categories AS category ON category.id = alert.categoryId 
->  LEFT JOIN analyzers AS analyzer ON analyzer.id = alert.analyzerId 
->  LEFT JOIN nodes AS analyzerNode ON analyzerNode.id = alert.analyzerNodeId 
->  LEFT JOIN nodes AS sourceNode ON sourceNode.id = alert.sourceNodeId 
->  LEFT JOIN nodes AS targetNode ON targetNode.id = alert.targetNodeId 
->  LEFT JOIN users AS sourceUser ON sourceUser.id = alert.sourceUserId 
->  LEFT JOIN users AS targetUser ON targetUser.id = alert.targetUserId 
->  LEFT JOIN `references` AS reference ON reference.id = alert.referenceId 
-> WHERE category.severity = 'high' 
-> ORDER BY alert.id DESC 
-> LIMIT 40 OFFSET 0; 
  • | id | select_type |テーブル|タイプ| possible_keys |キー| key_len | ref |行|余分な|
  • | 1 |シンプル|カテゴリ| ref | PRIMARY、severityIndex | severityIndex | 2 | const | 8 |どこで使用するか。一時的な使用。 filesortの使用|
  • | 1 |シンプル|アラート| ref | categoryIndex | categoryIndex | 8 | am.category.id | 3883428 | |
  • | 1 |シンプル|アナライザ| eq_ref | PRIMARY | PRIMARY | 8 | am.alert.analyzerId | 1 | |
  • | 1 |シンプル| analyzerNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.analyzerNodeId | 1 | |
  • | 1 |シンプル| sourceNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.sourceNodeId | 1 | |
  • | 1 |シンプル| targetNode | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.targetNodeId | 1 | |
  • | 1 |シンプル| sourceUser | eq_ref | PRIMARY | PRIMARY | 8 | am.alert.sourceUserId | 1 | |
  • | 1 |シンプル| targetUser | eq_ref | PRIMARY | PRIMARY | 8 | am.alert。targetUserId | 1 | |
  • | 1 |シンプル|リファレンス| eq_ref | PRIMARY | PRIMARY | 8 | am.alert.referenceId | 1 | |

実行の最初のステップとして制限があり、もう1つでは制限があることに注意してください。とにかくこのような制限を最初に実行するようにMySQLクエリオプティマイザを強制することはありますか?

+0

最初の結合を「INNER JOIN」に変更するとどうなりますか? _あなたの 'WHERE'は効果的にそれを効果的にするので、あなたの結果を変えるべきではありません。 – Uueerdo

+0

オプティマイザのヒントを使うことができます:https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html 。もう1つの方法は、悪い実行計画を良好にするための索引を追加する方法と、オプティマイザが別の計画を作成する方法です。 – mm759

+0

INNER JOINは、クエリまたは実行計画のパフォーマンスを変更しませんでした。オプティマイザのヒントについては、私の問題の解決策として私を掴んだことは正直には分かりませんでした。私は提案に開放されています。 – Agricola

答えて

0

ORDER BYとLIMITを使用する場合は、order byがインデックスにあることが重要です。

オプティマイザが別のインデックスを取得することを決定した場合、結果が取得され、さらに多くのレコードがスキャンされるため、制限が適用されません。

場合によっては、「情報」アラートよりも「高」の方が少なく、オプティマイザはカテゴリテーブルの重大度インデックスを優先します。

アラートテーブルのPKを強制的にFORCE INDEXにするか、カテゴリテーブルの重大度インデックスを無視してみます。

このarticleについてはお役に立ちます。