此查询ONE
SELECT * FROM TEST_RANDOM WHERE EMPNO >= '236400' AND EMPNO <= '456000';
正在以成本1927运行。
这个查询TWO:
SELECT * FROM TEST_RANDOM WHERE EMPNO = '236400';
正在以1924的成本运行。
这个表TEST_RANDOM
有1.000.000行,我创建这个表是为了:
Create table test_normal (empno varchar2(10), ename varchar2(30), sal number(10), faixa varchar2(10));
Begin
For i in 1..1000000
Loop
Insert into test_normal values(
to_char(i), dbms_random.string('U',30),
dbms_random.value(1000,7000), 'ND'
);
If mod(i, **10000)** = 0 then
Commit;
End if;
End loop;
End;
Create table test_random
as
select /*+ append */ * from test_normal order by dbms_random.random;
我在字段EMPNO
中创建了一个B树索引,因此:
CREATE INDEX IDX_RANDOM_1 ON TEST_RANDOM (EMPNO);
在此之后,查询TWO得到改进,并且成本变为4。
但是查询ONE没有改进,因为Oracle数据库忽略了它,出于某种原因,Oracle数据库理解此查询不值得使用带索引的计划执行...
我的问题是:我们可以做些什么来提高这个查询ONE的性能呢?因为索引的解决方案没有解决,而且它的成本仍然很高...
1条答案
按热度按时间qncylg1j1#
对于此查询,Oracle不使用索引,因为优化器正确地估计了行数,并正确地决定了全表扫描会更快或更有效。
B树索引通常只在可用于返回一小部分行时才有用,而您的第一个查询返回了大约25%的行。很难说理想的行百分比是多少,但25%几乎总是太大了。在我的系统上,当查询返回1723行时,执行计划将从全表扫描更改为索引范围扫描-但这个数字可能会有所不同。
全表扫描在检索大部分行时优于索引的原因有以下几个:
1.**单块与多块:**在Oracle中,与几乎所有计算机系统一样,一次检索多个数据块(顺序访问)比一次检索一个随机数据块(随机访问)要快得多。
1.**聚簇因子:**Oracle将所有行存储在块中,块通常为8 KB大小,类似于页。如果索引的效率非常低,例如索引是基于随机排序的数据构建的,并且两次顺序读取很少从同一个块中读取,则从索引中阅读25%的行可能仍然需要读取100%的表块。
1.**算法复杂性:**全表扫描将数据作为简单的堆读取,即
O(N)
。单个索引访问要快得多,为O(LOG(N))
。但随着索引访问次数的增加,这种好处逐渐消失,直到最终使用的索引为O(N * LOG(N))
。可以执行以下操作来提高不使用索引时的性能:
1.**分区:**分区是从表中检索大部分数据的理想解决方案(但必须获得许可)。使用分区时,Oracle会将逻辑表拆分为多个物理表,查询只能从所需的分区中读取。这可以带来多块读取的好处,但仍会限制扫描的数据量。
1.**并行性:**让Oracle工作得更辛苦,而不是更聪明。但是对于这么小的表来说,并行性可能不值得这么麻烦。
1.**实体化视图:**创建只存储所需内容的表。
1.**对数据进行排序:**通过按相关列对表数据进行排序(而不是随机排序)来改进索引聚簇因子。在您的情况下,请将
order by dbms_random.random
替换为order by empno
。根据您的版本和平台,可以使用实体化区域Map来保持表的排序。1.**压缩:**收缩表以加快读取整个表的速度。
对于可能的小性能问题,这是相当多的信息。在您进入这个兔子洞之前,可能值得问一下,您是否真的有一个重要的性能问题(通过时钟或资源消耗来衡量),或者您只是通过查看有点无意义的成本度量来钓性能问题?