4

我有 3 个innodb表,比如 A、B 和 C。有一个查询可以连接这三个表以生成结果。

SELECT A.a, B.b, C.c
from A 
join B on A.id = B.a_id 
join C on C.id = B.c_id
where A.a = 'example' and B.b < 10;

一开始,当我使用“解释”命令测试查询时,它给了我以下顺序:

B -- C -- A

然而,这不是最优的。所以我对所有表运行“分析表”,它给了我:

A -- B -- C

,我相信这是正确的顺序。

然后我将SQL部署到生产中,1​​个月后,无缘无故的执行计划切换回了不好的选项,即B--C--A。之后,我ANALYZE TABLE又尝试了几次运行,但这一次,结果让我感到困惑。有时它也给我 B--C--A,有时它给我 A--B--C,有时甚至是其他执行计划。

所以我的问题是:

  1. 为什么部署后执行计划会发生变化?
  2. 除了固定执行计划(数据更新和变化很快,所以未来可能会改变最优计划),有没有办法保证最优计划总是得到保证?
4

1 回答 1

6

优化器根据有关表大小、基数、值分布、索引等的内存统计信息来选择重新排序表和使用索引。这些统计信息是估计值,并非在任何时候都绝对准确。

InnoDB 会不时更新其统计信息,这就是您在运行 ANALZYE TABLE 时可能导致发生的情况。

但是,在某些情况下,内存中的统计信息正处于使优化器做出不同选择的风口浪尖,因此您会看到这种翻转行为。

您可以通过在查询中指定索引提示来覆盖优化器选择索引的默认算法。

您可以通过指定来覆盖优化器对表重新排序的默认算法STRAIGHT_JOIN。这意味着您希望它按照您在 FROM 子句中给它们的顺序读取表,并且不要重新排序它们。

您可以使用 STRAIGHT_JOIN 作为查询修饰符(如 DISTINCT)。把它放在 SELECT 之后:

SELECT STRAIGHT_JOIN A.a, B.b, C.c
from A 
join B on A.id = B.a_id 
join C on C.id = B.c_id
where A.a = 'example' and B.b < 10;

但要小心不要过于随意地使用索引提示或连接提示。在数据的大小和分布发生一点点变化之后,优化器可能会在下周避免翻转行为。如果您的代码中有太多覆盖,您可能会阻止优化器做得更好!

于 2013-01-30T23:51:41.883 回答