【发布时间】:2017-03-11 15:32:16
【问题描述】:
我有一个带有限制的查询,该限制根据 where close 中的值提供截然不同的执行时间。我正在使用 MySQL 5.1,并在我最大的表“警报”中查看大约 100,000,000 条记录。
当我看到这个执行计划时,我很高兴,我的查询不到一秒钟:
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;
- |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 |
- | 1 |简单 |警报 |索引 |类别索引 |初级 | 8 |空 | 40 | |
- | 1 |简单 |类别 | eq_ref |初级,严重性指数 |初级 | 8 | am.alert.categoryId | 1 |使用位置 |
- | 1 |简单 |分析仪| eq_ref |初级 |初级 | 8 | am.alert.analyzerId | 1 | |
- | 1 |简单 |分析器节点 | eq_ref |初级 |初级 | 8 | am.alert.analyzerNodeId | 1 | |
- | 1 |简单 |源节点 | eq_ref |初级 |初级 | 8 | am.alert.sourceNodeId | 1 | |
- | 1 |简单 |目标节点 | eq_ref |初级 |初级 | 8 | am.alert.targetNodeId | 1 | |
- | 1 |简单 |来源用户 | eq_ref |初级 |初级 | 8 | am.alert.sourceUserId | 1 | |
- | 1 |简单 |目标用户 | eq_ref |初级 |初级 | 8 | am.alert.targetUserId | 1 | |
- | 1 |简单 |参考 | eq_ref |初级 |初级 | 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;
- |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 |
- | 1 |简单 |类别 |参考 |初级,严重性指数 |严重性指数 | 2 |常量 | 8 |使用哪里;使用临时的;使用文件排序 |
- | 1 |简单 |警报 |参考 |类别索引 |类别索引 | 8 | am.category.id | 3883428 | |
- | 1 |简单 |分析仪| eq_ref |初级 |初级 | 8 | am.alert.analyzerId | 1 | |
- | 1 |简单 |分析器节点 | eq_ref |初级 |初级 | 8 | am.alert.analyzerNodeId | 1 | |
- | 1 |简单 |源节点 | eq_ref |初级 |初级 | 8 | am.alert.sourceNodeId | 1 | |
- | 1 |简单 |目标节点 | eq_ref |初级 |初级 | 8 | am.alert.targetNodeId | 1 | |
- | 1 |简单 |来源用户 | eq_ref |初级 |初级 | 8 | am.alert.sourceUserId | 1 | |
- | 1 |简单 |目标用户 | eq_ref |初级 |初级 | 8 | am.alert.targetUserId | 1 | |
- | 1 |简单 |参考 | eq_ref |初级 |初级 | 8 | am.alert.referenceId | 1 | |
请注意,第一个执行的第一步是有限制,而另一个没有。有没有强制 MySQL 查询优化器像这样首先执行限制?
【问题讨论】:
-
如果将第一个联接更改为
INNER JOIN,会发生什么情况? 您的WHERE无论如何都有效地使它成为一个,所以它不应该改变您的结果。 -
您可以使用优化器提示:dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html。另一种方法可能是添加索引,使错误的执行计划变得更好,或者让优化器创建另一个计划。
-
INNER JOIN 没有改变查询或执行计划的性能。至于优化器提示,老实说,我没有看到任何能抓住我的东西作为我的问题的解决方案(让限制先行)。我愿意接受建议。