Oracle 在解析 prepared statement 时是否选择了默认的执行计划?
Does Oracle chose a default execution plan when parsing a prepared statement?
根据这个Oracle documentation,我可以假设优化器推迟了硬解析并且直到第一次执行准备好的语句时才生成执行计划:
"The answer is a phenomenon called bind peeking. Earlier, when you ran that query with the bind variable value set to 'NY', the optimizer had to do a hard parse for the first time and while doing so it peeked at the bind variable to see what value had been assigned to it."
但是当使用绑定参数为准备好的语句执行 EXPLAIN PLAN 时,我们得到了一个执行计划。在他的网站上,Markus Winand 说:
"When using bind parameters, the optimizer has no concrete values available to determine their frequency. It then just assumes an equal distribution and always gets the same row count estimates and cost values. In the end, it will always select the same execution plan."
哪个是真的?使用均匀分布值模型准备语句时是否生成执行计划,或者硬解析是否推迟到第一次执行时间。
根据 Tom Kyte 绑定窥视发生在硬解析阶段,这与您 post 中的第一个引号一致。在 11g 中,优化器甚至能够为不同的绑定范围提出不同的计划,这直接与第二个引用相矛盾(尽管公平地说他是在谈论绑定变量而不是专门查看)。
The query in the application uses bind values that drive it to one plan or the other consistently. It is only when the plan flip-flops between two radically different execution paths, and for some segment of users, that you have a really bad plan. In such cases, Oracle Database 11g might be the right answer for you, because it accommodates multiple plans.
第一次绑定查看实际上发生在第一次执行时。计划优化被推迟,它不会发生在准备阶段。稍后可能会发生另一个 bind peek。通常对于 VARCHAR2,当您绑定两个完全不同的值时(即第一个值的长度为 1 个字节,后面的值为 10 个字节),优化器会再次查看,它可能会产生一个新的计划。在 Oracle 12 中,它得到了更多扩展,它具有自适应连接方法。所以优化器建议使用 NESTED LOOPs,但是当它实际执行的行数比估计的多时,它会立即切换到 HASH join。它不像自适应游标共享,您需要先犯错误才能生成新的执行计划。
准备好的语句也是一件非常重要的事情。因为这些只是重新执行与第一次执行时创建的相同的游标。他们将始终执行相同的计划,不会有任何适应。对于适应和替代执行计划,至少必须进行 SOFT 解析。因此,如果计划因任何原因从共享池中老化或失效。
说明计划不是游标,它永远不会考虑绑定变量。它只是在您可以看到绑定变量信息的地方显示光标。
您可以在 V$SQL_BIND_CAPTURE.
中找到有关捕获的绑定值的实际信息
一般来说,从 11g 开始的 Oracle 行为最好用自适应游标共享来描述(参见 http://docs.oracle.com/database/121/TGSQL/tgsql_cursor.htm#BGBJGDJE)
对于JDBC(Thin Driver)具体来说:当使用PreparedStatements时,在执行步骤之前没有生成任何计划。
参见以下示例:
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_MODULE_INDEX] = "adaptiveCSTest";
((OracleConnection) conn).setEndToEndMetrics(metrics, (short) 0);
String getObjectNames = "select object_name from key.objects where object_type=?";
PreparedStatement objectNamesStmt = conn.prepareStatement(getObjectNames);
// module set, but statement not parsed
objectNamesStmt.setString(1, "CLUSTER");
// same state
ResultSet rset1 = objectNamesStmt.executeQuery();
// statement parsed and executed
这个讨论遗漏了关于绑定变量、解析和绑定窥视的一个非常重要的观点;这是直方图!只有当相关列具有直方图时,绑定变量才会成为问题。没有直方图,就没有必要查看值。 Oracle 没有关于数据分布的信息,只会使用纯数学(不同的值、空值的数量、行数等)来找到有问题的过滤器的选择性。
绑定和直方图在逻辑上是相反的。您使用绑定变量为所有查询获取一个执行计划。您使用直方图为不同的搜索值获取不同的执行计划。 Bind peeking 试图克服这个问题。但它在这方面做得不是很好。许多人实际上将绑定查看功能描述为 "a bug"。 Oracle 11g 中出现的自适应游标共享可以更好地解决这个问题。
其实我看到周围有很多直方图。我通常禁用直方图(方法 opt=>'for all columns size 1',并且只在我真正需要它们时才创建它们。
然后回到原来的问题:"Does Oracle choose a default execution plan when parsing a prepared statement?"
解析不是一个activity。解析涉及语法检查、语义分析(表和列是否存在,您是否有权访问表)、查询重写(Oracle 可能会以更好的方式重写查询 - 例如 - 如果我们使用过滤器 a=b 和 b =c,那么Oracle就可以加上过滤器a=c),当然也可以找到执行计划。我们实际上区分不同类型的解析——软解析和硬解析。硬解析是 Oracle 还必须为查询创建执行计划的地方。这是一个非常昂贵的activity。
回到问题。解析并不真正关心您是否使用绑定变量。不同之处在于,如果您使用绑定,您可能只需要进行软解析。使用绑定变量,您的查询每次 运行 时看起来都一样(因此得到相同的 hash_value)。当您 运行 查询时,Oracle 将检查(在库缓存中)以查看是否已准备好执行您的查询的计划。这不是默认计划,而是一个已经存在的计划,因为其他人已经执行了相同的查询(并让 Oracle 执行硬解析为该查询生成执行计划)并且执行计划尚未从缓存中老化.这不是默认计划。这只是优化器在解析时认为是您的查询的最佳选择的计划。
当您使用 Oracle 12c 时,它实际上变得更加复杂。在 12 Oracle 中有 Adaptive Execution 计划——这意味着执行计划有一个替代方案。它可以从嵌套循环开始,如果它意识到基数估计有误,它可以在查询执行过程中切换到散列连接。它还具有称为自适应统计和 sql 计划指令的东西。所有这些都是为了让优化器和 Oracle 在 运行 宁您的 SQL 时做出更好的选择 :-)
根据这个Oracle documentation,我可以假设优化器推迟了硬解析并且直到第一次执行准备好的语句时才生成执行计划:
"The answer is a phenomenon called bind peeking. Earlier, when you ran that query with the bind variable value set to 'NY', the optimizer had to do a hard parse for the first time and while doing so it peeked at the bind variable to see what value had been assigned to it."
但是当使用绑定参数为准备好的语句执行 EXPLAIN PLAN 时,我们得到了一个执行计划。在他的网站上,Markus Winand 说:
"When using bind parameters, the optimizer has no concrete values available to determine their frequency. It then just assumes an equal distribution and always gets the same row count estimates and cost values. In the end, it will always select the same execution plan."
哪个是真的?使用均匀分布值模型准备语句时是否生成执行计划,或者硬解析是否推迟到第一次执行时间。
根据 Tom Kyte 绑定窥视发生在硬解析阶段,这与您 post 中的第一个引号一致。在 11g 中,优化器甚至能够为不同的绑定范围提出不同的计划,这直接与第二个引用相矛盾(尽管公平地说他是在谈论绑定变量而不是专门查看)。
The query in the application uses bind values that drive it to one plan or the other consistently. It is only when the plan flip-flops between two radically different execution paths, and for some segment of users, that you have a really bad plan. In such cases, Oracle Database 11g might be the right answer for you, because it accommodates multiple plans.
第一次绑定查看实际上发生在第一次执行时。计划优化被推迟,它不会发生在准备阶段。稍后可能会发生另一个 bind peek。通常对于 VARCHAR2,当您绑定两个完全不同的值时(即第一个值的长度为 1 个字节,后面的值为 10 个字节),优化器会再次查看,它可能会产生一个新的计划。在 Oracle 12 中,它得到了更多扩展,它具有自适应连接方法。所以优化器建议使用 NESTED LOOPs,但是当它实际执行的行数比估计的多时,它会立即切换到 HASH join。它不像自适应游标共享,您需要先犯错误才能生成新的执行计划。
准备好的语句也是一件非常重要的事情。因为这些只是重新执行与第一次执行时创建的相同的游标。他们将始终执行相同的计划,不会有任何适应。对于适应和替代执行计划,至少必须进行 SOFT 解析。因此,如果计划因任何原因从共享池中老化或失效。
说明计划不是游标,它永远不会考虑绑定变量。它只是在您可以看到绑定变量信息的地方显示光标。
您可以在 V$SQL_BIND_CAPTURE.
中找到有关捕获的绑定值的实际信息一般来说,从 11g 开始的 Oracle 行为最好用自适应游标共享来描述(参见 http://docs.oracle.com/database/121/TGSQL/tgsql_cursor.htm#BGBJGDJE)
对于JDBC(Thin Driver)具体来说:当使用PreparedStatements时,在执行步骤之前没有生成任何计划。
参见以下示例:
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_MODULE_INDEX] = "adaptiveCSTest";
((OracleConnection) conn).setEndToEndMetrics(metrics, (short) 0);
String getObjectNames = "select object_name from key.objects where object_type=?";
PreparedStatement objectNamesStmt = conn.prepareStatement(getObjectNames);
// module set, but statement not parsed
objectNamesStmt.setString(1, "CLUSTER");
// same state
ResultSet rset1 = objectNamesStmt.executeQuery();
// statement parsed and executed
这个讨论遗漏了关于绑定变量、解析和绑定窥视的一个非常重要的观点;这是直方图!只有当相关列具有直方图时,绑定变量才会成为问题。没有直方图,就没有必要查看值。 Oracle 没有关于数据分布的信息,只会使用纯数学(不同的值、空值的数量、行数等)来找到有问题的过滤器的选择性。
绑定和直方图在逻辑上是相反的。您使用绑定变量为所有查询获取一个执行计划。您使用直方图为不同的搜索值获取不同的执行计划。 Bind peeking 试图克服这个问题。但它在这方面做得不是很好。许多人实际上将绑定查看功能描述为 "a bug"。 Oracle 11g 中出现的自适应游标共享可以更好地解决这个问题。
其实我看到周围有很多直方图。我通常禁用直方图(方法 opt=>'for all columns size 1',并且只在我真正需要它们时才创建它们。
然后回到原来的问题:"Does Oracle choose a default execution plan when parsing a prepared statement?" 解析不是一个activity。解析涉及语法检查、语义分析(表和列是否存在,您是否有权访问表)、查询重写(Oracle 可能会以更好的方式重写查询 - 例如 - 如果我们使用过滤器 a=b 和 b =c,那么Oracle就可以加上过滤器a=c),当然也可以找到执行计划。我们实际上区分不同类型的解析——软解析和硬解析。硬解析是 Oracle 还必须为查询创建执行计划的地方。这是一个非常昂贵的activity。
回到问题。解析并不真正关心您是否使用绑定变量。不同之处在于,如果您使用绑定,您可能只需要进行软解析。使用绑定变量,您的查询每次 运行 时看起来都一样(因此得到相同的 hash_value)。当您 运行 查询时,Oracle 将检查(在库缓存中)以查看是否已准备好执行您的查询的计划。这不是默认计划,而是一个已经存在的计划,因为其他人已经执行了相同的查询(并让 Oracle 执行硬解析为该查询生成执行计划)并且执行计划尚未从缓存中老化.这不是默认计划。这只是优化器在解析时认为是您的查询的最佳选择的计划。 当您使用 Oracle 12c 时,它实际上变得更加复杂。在 12 Oracle 中有 Adaptive Execution 计划——这意味着执行计划有一个替代方案。它可以从嵌套循环开始,如果它意识到基数估计有误,它可以在查询执行过程中切换到散列连接。它还具有称为自适应统计和 sql 计划指令的东西。所有这些都是为了让优化器和 Oracle 在 运行 宁您的 SQL 时做出更好的选择 :-)