为啥索引不用于某些值?

Posted

技术标签:

【中文标题】为啥索引不用于某些值?【英文标题】:Why index is not used for some values?为什么索引不用于某些值? 【发布时间】:2014-01-29 15:08:56 【问题描述】:

我们有包含 email 和 siteId 列的“Customers”表。并且有一个由这两列组成的索引。因此,在通过电子邮件执行查询和过滤时,相关查询总是使用此索引,例如:

select * from customers where email = 'someEmail@gmail.com';

我们可以确保使用了索引,因为我们已经使用解释计划检查了查询。现在,我们昨天有一个有趣的案例。带有子句“email = 'random@gmail.com'”的相同查询根本没有使用索引。解释计划显示 oracle 进行了全面扫描。该表包含数百万条记录,因此这个没有索引的查询达到 30 秒。有趣的部分查询是否将索引与任何其他电子邮件值一起使用(ransom@gmail.com、randon@gmail.com)。oracle 不使用索引的原因是什么?

我的一个原因是索引不包含这样的值,所以首先 oracle 遍历索引然后进行全扫描,但随后解释计划应该显示它执行了索引扫描。在我们的例子中,解释计划只显示完整扫描。另一个原因是它与 db restore 有某种关系,但也不确定索引怎么可能不包含这样的值。

UPDATE 恢复后的所有表的 last_analyzed 日期值为一周前。在电子邮件的这种特殊情况下,由于客户隐私,所有电子邮件在更新后都会更新为一些随机值。客户表有几百万条记录,所以是的,恢复后发生了相当大的变化,但仍然不明白它与不使用电子邮件有什么关系,因为这个客户表在恢复之前已经有数百万条记录。

UPDATE2在我们对表执行收集统计信息后使用索引。

UDATE3 好吧,客户表中的所有电子邮件值都是唯一的,因此 CBO 不太可能决定此类特定电子邮件的行数过多。只是有很多电子邮件具有相同的起始字符“随机...”,但这不应该被视为相同的值,对吧?

【问题讨论】:

表是否包含频繁的插入和删除? 如果没有找到索引命中就不会进行全扫描;它会做一个或另一个,而不是两者兼而有之。 (它是进行全索引扫描还是全表扫描?)假设您的统计数据是最新的,索引是否有直方图,并且您是否有很多与行为不同的地址相似的地址? 我同意 Alex 的观点,这可能是由于特定电子邮件 ID 的基数很高,Oracle 将进行全表扫描。 是在电子邮件更新为随机值之前最后一次收集的统计信息吗?如果是这样,最初可能有很多相似(或相同)的值,而您的更新/随机值恰好与流行的原始选择一致。如果收集统计数据解决了它,那就太好了*8-) 【参考方案1】:

服务器不会查看索引中的数据,如果没有找到则扫描表。在它做任何事情之前,它会查看统计信息,看看它是否应该使用索引或扫描表。我猜想,由于某种原因,电子邮件地址通过了,使 oracle 认为扫描表比查看索引更好。有太多因素无法得出准确的结论。查看统计信息Oracle Doc on Stats。您可能会考虑更改索引的统计数据所涵盖的数据量,这会有所帮助。

【讨论】:

从我以looking at histograms 开头的描述来看,作为做出该决定的原因。 @AlexPoole 有趣的东西,谢谢。另请参阅更新后的问题。【参考方案2】:

直方图只考虑第一个 32 个字节。相同的起始字符有多大?详情请见this Oracle Optimizer post。

将唯一字符移动到字符串的开头或禁用直方图,如下所示:

begin
  dbms_stats.set_table_prefs
  (
    '<schema>',
    'CUSTOMERS',
    'METHOD_OPT',
    'FOR ALL COLUMNS SIZE AUTO, FOR COLUMNS SIZE 1 EMAIL'
  );
end;
/

【讨论】:

有趣的一点,但在我们的例子中,以“random..”开头的电子邮件只包含 6 个相同的字节(随机),所以直方图也应该考虑其他字符。

以上是关于为啥索引不用于某些值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥索引视图不能有 MAX() 聚合?

数据库索引

根据不均匀的日期重新索引熊猫数据框,然后用 groupby 和空白填充某些值

为啥 TIC-80 让我尝试索引零值错误?

当我使用“或”条件时,为啥我的查询使用过滤而不是索引条件?

Vue数组更新,为啥不能通过索引直接设置一个值