Oracle没有为长键使用唯一索引


16

我的测试数据库中有一个包含25万行的表。(有几亿个产品正在生产,我们在那里也可以观察到相同的问题。)该表具有nvarchar2(50)字符串标识符(不是null),并且具有唯一索引(不是PK)。

标识符由第一部分组成,该部分在我的测试数据库中具有8个不同的值(在生产中大约有1000个),然后是@符号,最后是一个1到6位数字的数字。例如,可能有5万行以“ ABCD_BGX1741F_2006_13_20110808.xml @”开头,然后是5万个不同的数字。

当我根据其标识符查询单行时,基数估计为1,成本非常低,可以正常工作。当我在一个IN表达式或一个OR表达式中查询带有多个标识符的多行时,索引的估计是完全错误的,因此使用了全表扫描。如果我用一个索引强加索引,它会非常快,实际上全表扫描的执行速度要慢一个数量级(并且生产速度要慢得多)。因此,这是一个优化程序问题。

作为测试,我使用完全相同的DDL和完全相同的内容复制了该表(在相同的模式+表空间中)。我在第一个表上重新创建了唯一索引以取得良好效果,并在克隆表上创建了完全相同的索引。我做了一个DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);。您甚至可以看到索引名称是连续的。因此,现在两个表之间的唯一区别是,第一个表是在很长一段时间内以随机顺序加载的,块分散在磁盘上(与其他几个大表一起放在一个表空间中),第二个表是成批加载的插入选择。除此之外,我无法想象任何区别。(自上次大删除以来,原始表已缩小,此后没有一个删除。)

这是病假表和克隆表的查询计划(黑色笔刷下的字符串在图片中相同,灰色笔刷下的字符串也一样):

查询计划

(在此示例中,有1867行以黑色刷过的标识符开头。2行查询产生的基数为1867 * 2,3行查询产生的基数为1867 * 3,依此类推。巧合的是,Oracle似乎并不在乎标识符的结尾。)

什么可能导致此行为?显然,在生产中重新创建表将非常昂贵。

USER_TABLES:http : //i.stack.imgur.com/nDWze.jpg USER_INDEXES:http : //i.stack.imgur.com/DG9um.jpg 我只更改了架构和表空间名称。您可以看到表名和索引名与查询计划屏幕快照上的相同。

Answers:


7

(这回答了另一个有关直方图为何不同的问题。)

默认情况下,将根据列偏斜以及是否在相关谓词中使用了列来创建直方图。复制DDL和数据还不够,工作负载信息也很重要。

根据 性能调整指南

删除表时,自动直方图收集功能使用的工作负载信息以及RESTORE _ * _ STATS过程使用的已保存统计信息历史记录都会丢失。没有此数据,这些功能将无法正常运行。

例如,这是一个数据倾斜但没有直方图的表:

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
NONE

运行相同的操作,但在收集统计信息之前进行查询,将生成直方图。

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
select count(*) from test1 where a = sysdate; --Only new line
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
FREQUENCY

2
非常简单的例子。您是否知道CBO为什么在唯一扫描中使用直方图进行基数估计而不是仅假设1?
杰克说请尝试topanswers.xyz 2014年

谢谢!:我做了一个完整的摄制与我的那种我的博客上的数据和查询的joco.name/2014/01/05/...
fejesjoco

@杰克我认为这是懒惰。Oracle工程师必须已经知道唯一索引的统计信息将具有与行相同数量的不同值,因此,与其他任何情况一样,1基数假设不是固定的,而只是从统计信息中使用。同样,通常,直方图胜过简单的统计数据。我的情况似乎很特殊,因为只有长键,但是我相信这在其他方面效果很好。
fejesjoco 2014年

@fejesjoco我认为JL的解释更有可能,因为在单次查找(不带in)的情况下,直方图也会胜过一般统计,不是吗?我认为CBO确实假设基数为1,但仅在最简单的情况下。我认为您可以使用一个很大的组件来解决整个问题,UNION ALL但是可能还有其他原因不这样做,JL在链接的博客文章中提到了其他可能的解决方法。
杰克说请尝试topanswers.xyz 2014年

1
要考虑的另一个小谜团-首先如何创建此直方图?Oracle似乎仅考虑到具有重复项的列,这显然是歪斜的,这显然是唯一列无法做到的。是有人故意(不太可能)构建了直方图,还是有人收集了非推荐的统计数据method_opt=>'for all indexed columns'
乔恩·海勒2014年

8

我找到了解决方案!它是如此的美丽,我实际上学到了很多有关Oracle的知识。

一言以蔽之:直方图。

我开始阅读很多有关Oracle CBO工作原理的文章,偶然发现了直方图。我不太了解,所以我看了一下USER_HISTOGRAMS表,看看。患病表有几行,而克隆表几乎没有。对于病态表,8个不同的标识符起始部分各有一行。这是关键:它们在@符号前被截断为32个字符。就像我说的那样,键的第一部分是高度重复的,它们在@符号之后变得不同。

直方图似乎比简单的事实更强大,因为对于给定的值,唯一索引的基数始终为0或1。当我查询2行以上时,Oracle查看了直方图,它认为该标识符起始部分可能有成千上万个值,因此CBO偏离了正常。

我删除了旧表中该列的直方图,问题消失了!

更多阅读:https : //blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating



我没看到:)。因此,唯一奇怪的是为什么在第一个表中而不是在克隆表中有直方图,我以为collect_schema_stats更新了所有内容,显然没有。
fejesjoco 2014年

6

我通过电子邮件给乔纳森·刘易斯(Jonathan Lewis)了,并得到了非常有用的回复:

计算中的奇异性是基于字符的直方图受到限制的结果,尤其是:

http://jonathanlewis.wordpress.com/2010/10/13/frequency-histogram-5/ http://jonathanlewis.wordpress.com/2010/10/19/frequency-histograms-6/

查看示例,查询针对的是IN列表,而不是针对单行,因此我的最初猜测是,优化器使用通用策略来计算多行选择性,而不是针对某段代码使用特殊情况的代码主键上的IN列表。我想对于他们来说识别这种情况并不难,但是开发人员可能认为不值得这样做。

我强烈建议阅读他链接的博客文章,它们详细描述了您正在运行的直方图的局限性,例如:

结论:如果您的列中的字符串相当长且类似,那么它很适合作为频率直方图的候选者(例如,描述性很强的状态列),那么如果一个非常罕见的值看起来与一个非常受欢迎的值相同,则您会遇到问题值(最多前32个字符)。您可能会发现唯一的解决方案是更改合法值列表(尽管涉及虚拟列或基于函数的索引的各种策略可以绕过该问题)。


遗憾的是,直方图似乎是一个鲜为人知的功能,我想这是因为它对SQL开发人员来说太深了,并且大多数时候它们只是在工作,但是很高兴知道有很多相关资源,我只是没有在寻找正确的地方:)。Oracle削减32个字节并据此做出灾难性的决定是非常糟糕的。幸运的是,我不需要任何调整,删除直方图是一个完美的解决方案。键值是唯一的,我总是一次查找20个值,它仅适用于索引,并且具有确定性。但是,下次我不会使用长键。
fejesjoco 2014年

直方图在DBA中是众所周知的;)我喜欢这样一个事实,您似乎热衷于学习更深入的知识,并且真的认为您应该阅读JL的书,这非常好。CBO通常做得很好:总是会有一些边缘案例需要调查,但是要记住,即使没有中断,估计也始终只是估计。
杰克说请尝试topanswers.xyz 2014年

1
如果您运行常规统计工作(如默认安装的Oracle在全新安装中运行),您可能会发现直方图再次出现,您可能需要研究一种防止这种情况的方法(例如LOCK_TABLE_STATS
杰克说尝试上层答案。 xyz 2014年

我在回答中提到了一篇博客文章,其中包含有关如何防止列的直方图的说明。
fejesjoco 2014年

1
@杰克·道格拉斯(Jack Douglas),感谢您参与J.刘易斯并向我汇报!
Dimitre Radoulov 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.