Oracle绑定查询非常缓慢

3

我有一个Oracle绑定查询,在我的C#程序中执行非常缓慢(大约2分钟),但在SQL Developer中运行非常快。它有两个参数,可以命中表的索引:

select t.Field1, t.Field2
from theTable t
where t.key1=:key1
  and t.key2=:key2

另外,如果我删除绑定变量并创建动态SQL,它将像在SQL Developer中一样运行。

有什么建议吗?

顺便说一下,我正在使用ODP。


2
我想知道这是否与C#和SQL Developer中不同的优化器设置有关(例如,first_rows vs all_rows)。请参阅Tom Kyte的这篇文章获取更多信息。也许您可以检查v$sql和v$sql_shared_cursor,看看是否存在相同SQL语句的多行,如果是,则不同的优化器模式是否是问题所在? - Boneist
3个回答

2
如果您在SQL开发人员中用静态变量替换绑定变量,那么您实际上并没有运行相同的测试。确保使用绑定变量,如果它也很慢,那么您只是受到了错误的缓存执行计划的影响。更新该表的统计信息应该可以解决问题。
但是,如果您实际上在SQL开发人员中使用绑定变量,请继续阅读。 TLDR版本是ODP.net下运行的参数有时会导致稍微悲观一些的方法。从更新统计信息开始,但是让您的DBA捕获两种情况下的执行计划并进行比较以确认。
我将我的答案从这里转载:https://stackoverflow.com/a/14712992/852208,我考虑过标记为重复,但是您的标题更加简洁,因为它确定了查询在SQL开发人员中运行得很快。我欢迎关于处理方式的建议。
将以下内容添加到配置中将ODP.net跟踪信息发送到日志文件:
这可能只有在时间间隔很大的情况下才有用。机会是行实际上正在以更慢的速度到来。
尝试在连接字符串中添加“enlist=false”。我不认为这是一个解决方案,因为它实际上禁用了分布式事务,但它应该帮助您隔离问题。您可以从oracle forumns post中获取更多信息:
从ODP的角度来看,我们只能指出当OCI_ATR_EXTERNAL_NAME和OCI_ATR_INTERNAL_NAME在基础OCI连接上设置时(这是启用distrib tx支持时发生的情况),行为就会发生。
我猜您没有看到执行计划实际上是不同的(这意味着实际性能损失实际上发生在服务器上)ODP.net调用和SQL开发人员调用之间。让您的DBA跟踪连接并从ODP.net调用和直接从SQL Developer(或使用enlist=false参数)调用中获取执行计划。
如果确认不同的执行计划或者如果您想采取预防措施,请更新相关表的统计信息。在我的情况下,这纠正了问题,表明执行计划生成并不真正遵循不同类型连接的不同规则,而是在涉及分布式事务时成本分析略微悲观。查询提示以强制执行计划也是一个选择,但只能作为最后的手段。
最后,可能是网络问题。如果您的ODP.net安装正在使用新的oracle home(我预计除非您进行了一些后安装配置,否则会这样),则tnsnames.ora可能不同。 tnsnams中的主机名可能不是完全限定的,从而创建更多延迟以解决服务器。在这种情况下,我只期望第一次尝试(而不是后续尝试)会很慢,因此我认为这不是问题,但我认为应该提到。

我正在从应用程序复制SQL,并保留绑定变量。 - wcm

1
请检查查询返回的行数。如果数量很大,可能是C#在获取所有行,而其他工具则只是先取一部分。在这种情况下获取所有行可能需要更多的磁盘读取,导致速度变慢。为了检查这一点,请尝试在SQL Developer中运行:
SELECT COUNT(*) FROM (
    select t.Field1, t.Field2
      from theTable t
     where t.key1=:key1
       and t.key2=:key2
)

上述查询应该获取最大数量的数据库块。
在这种情况下,一个好用的工具是tkprof实用程序,它显示SQL执行计划,在上述情况下可能会有所不同(但不应该有)。
也有可能您意外连接到了不同的数据库。在这种情况下,比较查询结果是很好的。
由于您提出了“Bind is slow”,我假设您已经检查过没有使用绑定的SQL语句并且运行速度很快。99%的情况下使用绑定会使事情变得更好。请检查带有常量的查询是否可以运行得很快。如果是,则问题可能是key1或key2列的隐式转换(例如,t.key1是一个数字,而:key1是一个字符串)。

记录数量较少,介于10到100之间。 - wcm

1

在C#中,参数是否绑定到正确的数据类型?key1key2列是否为数字,但参数:key1:key2是字符串?如果是,查询可能会返回正确的结果,但需要进行隐式转换。这种隐式转换就像使用函数to_char(key1)一样,会阻止索引的使用。


字段key1和key2分别为CHAR(8)和CHAR(2)。参数也是CHAR(8)和CHAR(2)。 - wcm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接