1

我正在尝试为我的数据库类证明一个声明。我已经说过,使用位图索引,与不使用位图索引时相比,可以在包含仅包含枚举值(如性别)的列的表上更快地执行 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

4 回答 4

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
于 2013-01-10T17:21:26.623 回答
2

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

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

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

于 2013-01-10T17:59:30.253 回答
1
  1. 每列创建一个位图索引。
  2. 使用大量数据进行测试。
  3. 使用 DBMS_Xplan 查看执行计划
于 2013-01-10T14:32:11.237 回答
1

除了拥有足够大的数据集之外,请在执行每个查询之前查看一下清除缓存

于 2020-06-15T15:08:07.567 回答