数据库中的位图索引不会加速查询

时间:2013-01-10 14:28:40

标签: database oracle indexing

我正在尝试为我的数据库类证明一个语句。我已经声明使用位图索引,可以在包含仅包含枚举值(如性别)的列的表上比在不使用位图索引时更快地执行WHERE查询。

除此之外,我们试图证明它对包含许多不同的随机值的列的表没有帮助。

我们在Oracle SQLDeveloper中创建了两个表,如上所述填充了上述10000行中的信息。 Oracle的版本是v11。

我们使用过程在表格中存储信息。当枚举值随机设置为'S','M'或'L'时,另一个表的值完全是随机的(数字和字母)。

首先,我们运行了声明:

set timing on
SELECT * FROM INDEXTEST2 WHERE GENDER = 'M' AND MARRIED = 'N' AND CHILDREN = 'Y'

重复运行此脚本最终需要00.016秒。在此之后,我们通过右键单击表创建索引,然后创建索引。我们选择了所有列并检查了Bitmap。之后,我们再次运行查询几次,只是遇到相同的速度:大约00.015秒。我们也尝试删除索引,没有结果。我们是否错过了这里的某些内容,或者我们是否正确地做了所有事情,而你却看不出速度上的差异?

4 个答案:

答案 0 :(得分:2)

即使没有索引,查询也可能非常快。因此,您无法通过秒表来证明性能提升。

我创建了一个包含1.000.000行的测试表。没有任何索引的全表扫描在我们的机器上需要0.2秒。使用位图索引,需要0.09秒。但是,如果没有索引,它会扫描所有2000个数据库块,并使用位图索引,它需要读取60-70个块。

CREATE TABLE indextest (
  id       NUMBER      NOT NULL,
  gender   VARCHAR2(1) NOT NULL CHECK (gender   IN ('M','F')),
  married  VARCHAR2(1) NOT NULL CHECK (married  IN ('Y','N')),
  children VARCHAR2(1) NOT NULL CHECK (children IN ('Y','N'))
) NOLOGGING;

INSERT /*+ APPEND */ 
  INTO indextest (id, gender, married, children)
WITH 
  q1 AS (SELECT          level AS x1 FROM dual CONNECT BY level <= 1000),
  q2 AS (SELECT 1000*(level-1) AS x2 FROM dual CONNECT BY level <= 1000),
  q3 AS (SELECT x2+x1 AS id FROM q1, q2)
SELECT id, 
       CASE WHEN dbms_random.value < 0.5   THEN 'M' ELSE 'F' END as gender,
       CASE WHEN dbms_random.value < 0.3   THEN 'Y' ELSE 'N' END as married,
       CASE WHEN dbms_random.value < 0.2   THEN 'Y' ELSE 'N' END as children
  FROM q3;
COMMIT;
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'indextest');
ALTER TABLE indextest ADD CONSTRAINT pk_indextest PRIMARY KEY (id);
CREATE BITMAP INDEX bi_indextest_gender   ON indextest(gender);
CREATE BITMAP INDEX bi_indextest_married  ON indextest(married);
CREATE BITMAP INDEX bi_indextest_children ON indextest(children);

现在,如果您显示查询统计信息,例如在SQL * Plus中,您可以显示使用索引需要60-70个块读取(“一致获取”):

SET TIMING ON
SET LINE 300
SET AUTOTRACE TRACE EXPLAIN STAT
SELECT count(*) FROM indextest WHERE gender = 'M' AND married = 'N' AND children = 'Y';

Abgelaufen: 00:00:00.07

------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                  | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                       |     1 |     6 |    63   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |                       |     1 |     6 |            |          |
|   2 |   BITMAP CONVERSION COUNT    |                       |   125K|   732K|    63   (0)| 00:00:01 |
|   3 |    BITMAP AND                |                       |       |       |            |          |
|*  4 |     BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_CHILDREN |       |       |            |          |
|*  5 |     BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_GENDER   |       |       |            |          |
|*  6 |     BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_MARRIED  |       |       |            |          |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("CHILDREN"='Y')
   5 - access("GENDER"='M')
   6 - access("MARRIED"='N')


Statistiken
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         67  consistent gets
          0  physical reads
          0  redo size
        235  bytes sent via SQL*Net to client
        252  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

如果隐藏索引,Oracle需要扫描整个表:

ALTER INDEX bi_indextest_gender   INVISIBLE;
ALTER INDEX bi_indextest_married  INVISIBLE;
ALTER INDEX bi_indextest_children INVISIBLE;
SELECT count(*) FROM indextest WHERE gender = 'M' AND married = 'N' AND children = 'Y';

Abgelaufen: 00:00:00.15

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |     6 |   512   (6)| 00:00:07 |
|   1 |  SORT AGGREGATE    |           |     1 |     6 |            |          |
|*  2 |   TABLE ACCESS FULL| INDEXTEST |   125K|   732K|   512   (6)| 00:00:07 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("GENDER"='M' AND "MARRIED"='N' AND "CHILDREN"='Y')


Statistiken
----------------------------------------------------------
        292  recursive calls
          0  db block gets
       2289  consistent gets
          0  physical reads
          0  redo size
        235  bytes sent via SQL*Net to client
        252  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          6  sorts (memory)
          0  sorts (disk)
          1  rows processed

答案 1 :(得分:2)

尝试选择较少的行(相对于表中的所有行)。如果查询选择表中的大部分行,Oracle的查询优化器可能(正确地)决定只进行全表扫描实际上更快 - 如果它必须加载表堆的大部分或全部块,它也可以直接做到。

除此之外,您的测试集太小了。尝试一个足够大的组(添加几个零)以将执行时间推到秒表的分辨率之外(在Windows机器上通常为16毫秒)。更好的是,选择一个不完全适合缓存的大小。

但即使你做了所有这些,你仍可能会得到一些奇怪的结果,因为甲骨文的整体“聪明”。例如,Oracle 11g引入了query result cache,它可以完全扭曲重复查询的执行时间。

答案 2 :(得分:1)

  1. 每列创建一个位图索引。
  2. 使用大量数据进行测试。
  3. 使用DBMS_Xplan查看执行计划

答案 3 :(得分:1)

除了拥有足够大的数据集之外,还应查看执行每个查询之前清除缓存的情况