(不成功)测试Oracle SQL中索引的影响

时间:2011-11-14 03:54:21

标签: sql database oracle database-design

我正在学习SQL的所有内容,在阅读索引时,我自然希望创建一个简单的adhoc测试,以查看索引如何真正影响性能(而不仅仅是读取面值)。但是,我正在尝试设置的测试遇到一些大问题。简而言之,无论我有多大的表,无论有没有索引,查询都会很快完成,平均每次0.5秒。我希望我可以创建一些如此大的表,以至于某些查询需要很长时间才能执行,然后通过创建索引我可以减少执行时间,并将减少量与不同类型的索引进行比较。但是,我根本没有运气!以下是我设置示例表的方法:

CREATE TABLE EMPLOYEE
(EmpID  NUMBER(6),
Lname VARCHAR2(20),
Fname VARCHAR2(20),
Gender CHAR(1),
HomeState CHAR(2),
BirthDate DATE,
HiredDate DATE,
Occupation VARCHAR2(20),
Salary NUMBER(6),
NumDep NUMBER(1));

EMPLOYEE表有10个属性,我创建了一个单独的Java程序,使SQL命令用随机信息填充表。例如,以下是Java程序的输出,输入大小为3:

INSERT INTO EMPLOYEE VALUES (1, 'HENRY', 'VIRGINIA', 'F', 'MA', '14-NOV-1966', '27-APR-1987', 'MANAGER', '80514', '5');
INSERT INTO EMPLOYEE VALUES (2, 'PARSONS', 'KEVIN', 'M', 'KS', '11-DEC-1961', '14-JAN-2004', 'NURSE', '74416', '3');
INSERT INTO EMPLOYEE VALUES (3, 'GARRETT', 'JIMMY', 'M', 'NE', '24-MAR-1963', '20-MAY-1992', 'SERVICE', '87116', '2');

姓氏是从500个常用姓氏中随机选择的。 第一个名字是从100个性别特定的名字中随机选择的。 随机选择状态(显然是50个列表)。 日期是在一定的合理范围内随机选择的。 从20个常见工作列表中随机选择职业。 薪水是在50000到99999之间随机选择的(这导致一些不切实际的薪水/工作匹配,但这不是真正的重点)。 从属人数在0到5之间随机选择。

我认为上述设置的一个好处是域大小的范围。即可能有500个姓氏,可能有100个名字,50个州,20个工作等。我从阅读中知道,当可能值的数量小于记录总数时,索引效果更好,我希望能够用我的测试证明这一点。但是,正如我之前所说,我的测试失败了。

我开始只使用500条记录填充EMPLOYEE表,并发现我在SQL Developer(我用来连接到Oracle 11g的软件)中使用的每个查询(例如下面的代码)只用了0.5秒。

SELECT * FROM EMPLOYEE WHERE salary BETWEEN 60000 and 62000;

然后我尝试了1,000条记录,然后是10,000条记录,然后是100,000条记录,然后是500,000条记录(由于我的剪贴板和/或SQL Developer似乎无法处理如此大的脚本,因此我必须分批插入100,000条记录) ......结果仍然相同。它在0.5秒内解决了每个查询!这让我感到震惊,因为插入100,000条记录需要60多秒。此外,SQL Developer似乎无法在单个查询脚本执行中返回超过5,000个结果,如果我接近这个最大值,有时需要1.0秒而不是0.5秒,但我怀疑这主要是因为需要时间在脚本输出窗口中打印所有5,000个结果。

我已尝试过对各种属性的查询,并且我尝试使用和不使用这些属性的索引,并且索引似乎没有任何区别。我在这里非常失望。我期待我的测试能够产生更加切实的结果。我也希望能够使用这个主题,以及我需要尽快编写的类论文的测试结果,但是我的测试结果不会产生任何困难。有什么建议吗?

P.S。如果你这样做,感谢您阅读这篇文章!真的很抱歉......

4 个答案:

答案 0 :(得分:3)

如果你正在做SELECT *,那么无论如何,Oracle基本上都必须为你的表检索所有块,所以它很可能没有使用你的索引。要确定它是否正在使用您的索引,您需要查看解释计划或执行计划。这将告诉您Oracle选择获取数据的路线,全表扫描,索引扫描等等。

以下是有关在10g中使用EXPLAIN PLAN的一些文档。

至于SQL Dev中的时间,一旦Oracle读取了表的所有数据块,它就会将它们保存在缓存中一段时间​​。这是通常需要最长时间的IO的一部分。 SQL Dev中的5000限制可以在选项中的某处设置,但我忘了在哪里。

最好在SQL * Plus中执行此操作,并将AUTOTRACE设置为on。您可以通过这种方式获得实际IO调用的记录。我认为,寻找“db block gets”和“一致得到”。

实际上,如果没有执行计划或解释计划告诉我们Oracle在获取数据方面的确在做什么,很难判断它是否甚至使用了您的索引。

答案 1 :(得分:2)

在sqldev工作表中尝试autotrace(该按钮位于运行的第二位)。 sqldev中的Autotrace本质上是解释计划,修改了行源级别的执行统计信息。要注意的列是“经过时间”和“缓冲区获取”(逻辑IO)。您可能还希望观察每个阶段处理的行数。

这些块可以通过多层缓存:通过RDBMS,通过操作系统,通过磁盘缓存(你的驱动器是固态吗?);因此,您无法有效控制测试设置中的已用时间。然而,您将看到缓冲区获取的巨大差异,并且autotrace将详细公开在行源级别上发生的事情。总而言之:逻辑IO的性能指标比经过的时间要小得多。

编辑:您可能还需要更加关注测试细节。如果您正在测量客户端挂钟(这是sqldev在网格工具栏上显示的内容),那么这是最糟糕的时间指标。考虑测量服务器端的时序,最好是纯执行时间(查询运行时间减去软解析)。这是在不同的性能字典视图/ tkprof中显示的内容。其次,sqldev只提取前100行(或左右),而您似乎希望对全表扫描进行基准测试。考虑将select *替换为select count(col),其中col未被任何索引覆盖 - 其他聚合操作通常会造成可忽略的开销。

答案 2 :(得分:0)

我的猜测是你的数据被缓存在内存中,你的查询只是在不实际执行磁盘I / O的情况下检索数据。您需要在每个查询之间刷新缓冲池以查看是否存在问题:

alter system flush buffer_cache;

我假设这是您自己系统上的数据库,这样做不会影响其他任何人。

答案 3 :(得分:0)

我认为您应该考虑使用tkprof这将显示正在运行的查询的详细信息,以及您的服务器上运行的查询的执行计划(而不是您的查询的执行计划)客户,他们可以有所不同)

请参阅:http://download.oracle.com/docs/cd/B10500_01/server.920/a96533/sqltrace.htm

我也不确定我是否会沿着缓冲区冲洗路线走下去(因为这不是现实世界的场景)

确保正在读取查询的整个结果集而不在客户端中分页的一种简单方法是创建新表

e.g:

CREATE TABLE t1 AS 
SELECT * FROM employee

最后,您可能需要在填写表格后分析索引... e.g:

DBMS_STATS.GATHER_TABLE_STATS(     
    METHOD_OPT => 'FOR ALL INDEXED COLUMNS SIZE AUTO'
   ,OWNNAME => 'YOUR_SCHEMA'
   ,TABNAME => 'EMPLOYEE'      
   ,ESTIMATE_PERCENT => NULL     
   ,CASCADE => TRUE);  

请参阅:http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_stats.htm