为啥使用索引但sql仍然很慢
Posted
技术标签:
【中文标题】为啥使用索引但sql仍然很慢【英文标题】:Why used index but the sql is still slow为什么使用索引但sql仍然很慢 【发布时间】:2019-08-16 04:08:35 【问题描述】:有一个表ORG_HLD_INFO
,并且有一个索引:"ORG_HLD_INFO" ("HLD_UNI_CODE", "ISVALID", "ORG_UNI_CODE")
。现在执行下面的查询很慢,需要 3.26 秒(并且获取的所有行都是 466)
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE in (30004536568,...)
为什么这么慢?它应该使用索引,以及索引中选择的所有字段,因此无需查询表行。
附言
该表的总数为:109102083
以下是解释计划
【问题讨论】:
in (30004536568,...)
子句中有多少个值?如果它超过几个,那么索引几乎没用。
这是一个复合索引吗?
@VladimirBaranov 谢谢!通常在 100 左右,但为什么在这种情况下索引是无用的,如何改进它?
您能否提供执行的 SQL 监视器报告。在你知道发生了什么之前,一切都只是猜测......
@BobC 已添加解释计划
【参考方案1】:
您在 cmets 中说您的 IN
子句可能有大约 100 个值。
在这种情况下,索引通常没用。
让我们看一下您的查询的简化版本
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE in (3, 4, 5)
这相当于
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and
(HLD_UNI_CODE = 3 OR HLD_UNI_CODE = 4 OR HLD_UNI_CODE = 5)
这相当于
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 3
UNION ALL
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 4
UNION ALL
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 5
这里我们可以使用UNION ALL
,因为我们在选定的列中包含HLD_UNI_CODE
。如果不是,我们可能需要使用UNION
。
无论如何,关键是每个单独的简单查询都像
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 5
可以使用索引。从某种意义上说,引擎将在索引中查找以仅查找所需的行而不是扫描所有行。
在某些数据库中,优化器可能足够聪明,可以将IN
/OR
查询重写为更简单的索引查找的UNION
(如果IN
中的值很少)。完全不知道Oracle的优化器能不能做这种转换。
但是,当您有数百个这样的简单查询时,执行所有这些查找然后将它们放在一起很快就会变得过于昂贵,因此优化器选择扫描整个表;并且扫描表(或索引)仍然意味着读取所有 109102083 行并将过滤器应用于每一行。
在计划RANGE SCAN
中可以看到,不是seek,本质上就是读取所有行。你可以看到 Predicate(过滤器)和一堆OR
s。
您可以尝试将查询重写为数百个联合并检查它是否运行得更快,但查询可能变得过于复杂而无法解析。因此,即使您尝试手动进行此转换,也可能不可行,除非您找到一些技巧,例如在循环中运行数百个简单查询并将中间结果转储到临时表中。
【讨论】:
谢谢!全局分区索引在这种情况下有帮助吗? @zhguuowei,我没有分区索引的经验,但我怀疑他们能提供帮助。如果您可以将IN
子句中的值列表放入表中,则可以将查询重写为“强制”优化器进行数百次搜索。我尝试使用的关键思想是所谓的横向连接。我对Oracle了解不多,但它应该支持它。不过,我不知道 Oracle 的确切语法。你最好再问一个问题。但是,不能保证它会比全扫描更快。以上是关于为啥使用索引但sql仍然很慢的主要内容,如果未能解决你的问题,请参考以下文章