Bitmap 索引 vs. Btree 索引:如何选择以及何时使用?——35

——理解适当使用每个索引对性能的影响

Bitmap 索引 vs. B-tree 索引:如何选择以及何时使用?——1-5

Bitmap 索引 vs. B-tree 索引:如何选择以及何时使用?——2-5

Bitmap 索引 vs. B-tree 索引:如何选择以及何时使用?——3-5

Bitmap 索引 vs. B-tree 索引:如何选择以及何时使用?——4-5

Bitmap 索引 vs. B-tree 索引:如何选择以及何时使用?——5-5

本文内容

  • 步骤 5A(TEST_NORMAL 表 SAL 列创建 Bitmap 索引,执行等值和范围查询)
  • 步骤 5B(TEST_NORMAL 表 SAL 列创建 B-tree 索引,执行等值和范围查询)

步骤 5A(在 TEST_NORMAL 表 SAL 列 Bitmap 索引)


在 TEST_NORMAL 表 SAL 列上创建 Bitmap 索引,该列具有常规基数(cardinality)值,查看索引大小和聚类系数。

SQL> create bitmap index normal_sal_bmx on test_normal(sal);
 
索引已创建。
 
SQL> analyze table test_normal compute statistics for table for all indexes for
all indexed columns;
 
表已分析。
 
SQL>
 
SQL> select substr(segment_name,1,30) segment_name, bytes/1024/1024 "Size in MB"
  2  from user_segments
  3  where segment_name in ('TEST_NORMAL','NORMAL_SAL_BMX');
 
SEGMENT_NAME                                                 Size in MB
------------------------------------------------------------ ----------
NORMAL_SAL_BMX                                                        4
TEST_NORMAL                                                          50
 
SQL> select index_name, clustering_factor from user_indexes;
 
INDEX_NAME                     CLUSTERING_FACTOR
------------------------------ -----------------
RANDOM_EMPNO_IDX                          999823
NORMAL_EMPNO_IDX                            6210
NORMAL_SAL_BMX                              6001
 
SQL>

现在在 SAL 列上先进行等值查询:

SQL> set autot traceonly
SQL> select * from test_normal where sal=&sal;
输入 sal 的值:  1869
原值    1: select * from test_normal where sal=&sal
新值    1: select * from test_normal where sal=1869
 
已选择188行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 257953309
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                |   167 |  6179 |    35(0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | TEST_NORMAL    |   167 |  6179 |    35(0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS|                |       |       |         |          |
|*  3 |    BITMAP INDEX SINGLE VALUE | NORMAL_SAL_BMX |       |       |         |          |
-----------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("SAL"=1869)
 
 
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        188  consistent gets
          0  physical reads
          0  redo size
      11392  bytes sent via SQL*Net to client
        651  bytes received via SQL*Net from client
         14  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        188  rows processed
 
SQL>

然后,执行范围谓词查询:

SQL> select * from test_normal where sal between &sal1 and &sal2;
输入 sal1 的值:  1500
输入 sal2 的值:  2000
原值    1: select * from test_normal where sal between &sal1 and &sal2
新值    1: select * from test_normal where sal between 1500 and 2000
 
已选择83613行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 512490529
 
---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time|
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             | 83534 |  3018K|  1727   (2)| 00:00:21|
|*  1 |  TABLE ACCESS FULL| TEST_NORMAL | 83534 |  3018K|  1727   (2)| 00:00:21|
---------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("SAL"<=2000 AND "SAL">=1500)
 
 
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      11777  consistent gets
          0  physical reads
          0  redo size
    4462936  bytes sent via SQL*Net to client
      61833  bytes received via SQL*Net from client
       5576  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      83613  rows processed
 
SQL>

步骤 5B(在 TEST_NORMAL 表 SAL 列 B-tree 索引)


现在,删除 TEST_NORMAL 表 SAL 列的 Bitmap 索引,创建 B-tree 索引:

SQL> drop index normal_sal_bmx;
 
索引已删除。
 
SQL> create index normal_sal_idx on test_normal(sal);
 
索引已创建。
 
SQL> analyze table test_normal compute statistics for table for all indexes for
all indexed columns;
 
表已分析。
 
SQL> select substr(segment_name,1,30) segment_name, bytes/1024/1024 "Size in MB"
  2  from user_segments
  3  where segment_name in ('TEST_NORMAL','NORMAL_SAL_IDX');
 
SEGMENT_NAME                                                 Size in MB
------------------------------------------------------------ ----------
NORMAL_SAL_IDX                                                       17
TEST_NORMAL                                                          50
 
SQL> select index_name, clustering_factor from user_indexes;
 
INDEX_NAME                     CLUSTERING_FACTOR
------------------------------ -----------------
RANDOM_EMPNO_IDX                          999823
NORMAL_EMPNO_IDX                            6210
NORMAL_SAL_IDX                            986864
 
SQL>

可以看到,该列的 B-tree 索引比 Bitmap 索引大。聚类系数接近表中的行数。

现在先执行等值查询:

SQL> select * from test_normal where sal=&sal;
输入 sal 的值:  1869
原值    1: select * from test_normal where sal=&sal
新值    1: select * from test_normal where sal=1869
 
已选择188行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 4257100160
 
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |   167 |  6179 |   168   (0)| 00:00:03 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST_NORMAL    |   167 |  6179 |   168   (0)| 00:00:03 |
|*  2 |   INDEX RANGE SCAN          | NORMAL_SAL_IDX |   167 |       |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("SAL"=1869)
 
 
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        202  consistent gets
          0  physical reads
          0  redo size
      11392  bytes sent via SQL*Net to client
        651  bytes received via SQL*Net from client
         14  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        188  rows processed
 
SQL>

然后,执行范围查询:

SQL> select * from test_normal where sal between &sal1 and &sal2;
输入 sal1 的值:  1500
输入 sal2 的值:  2000
原值    1: select * from test_normal where sal between &sal1 and &sal2
新值    1: select * from test_normal where sal between 1500 and 2000
 
已选择83613行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 512490529
 
---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time|
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             | 83534 |  3018K|  1727   (2)| 00:00:21|
|*  1 |  TABLE ACCESS FULL| TEST_NORMAL | 83534 |  3018K|  1727   (2)| 00:00:21|
---------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("SAL"<=2000 AND "SAL">=1500)
 
 
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      11777  consistent gets
          0  physical reads
          0  redo size
    4462936  bytes sent via SQL*Net to client
      61833  bytes received via SQL*Net from client
       5576  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      83613  rows processed
 
SQL>

当输入不同值查询时,如下表所示,结果输出揭示 consistent gets 和 physical reads 的值一致。

表 5 TEST_NORMAL 表 SAL 列 Bitmap 和 B-tree 索引统计信息比较

  Bitmap B-tree
size   4MB   17MB
clustering factor   6001   986864
blocks    

表 6 TEST_NORMAL 表 SAL 列使用 Bitmap 和 B-tree 索引等值查询比较

BITMAP B-TREE Rows Fetched
Consistent Reads Physical Reads SAL (Equality) Consistent Reads Physical Reads  
  165    0   1869   177   164  
  169   163   3548   181    167  
  174   166   6500   187    172  
  75   69   7000   81    73  
  177   163   2500   190   175  

表 7 TEST_NORMAL 表 SAL 列使用 Bitmap 和 B-tree 索引范围查询比较

BITMAP B-TREE Rows Fetched
Consistent Reads Physical Reads SAL (Range) Consistent Reads Physical Reads  
  11778   5850   1500-2000   11778   3891   83743
  11765   5468   2000-2500   11765   3879   83328
  11753   5471   2500-3000   11753   3884   83318
  17309   5472   3000-4000   17309   3892   166999
  39398   5454   4000-7000   39398   3973   500520

优化器为所有不同输入的范围谓词查询,选择了全表扫描,完全不使用索引。而对等值谓词,优化器使用索引,consistent gets 和 physical reads 的值一致。

因此,可以得出结论,对于一个正常基数的列,优化器对这两种类型索引的选择是一样的,而 IO 之间没有重大差异。

基数(Cardinality) - 使用 bitmap 索引对低基数的列最合适:也就是说,不重复列的数量比表中行数小。如果一个列不重复值的数量小于表中行数的 1%,或如果一个列中的值被重复超过 100 次,那么该列就可以创建 bitmap 索引。如果在 WHERE 子句中设计复杂条件,那么即使具有较低重复数量列,从而具有较高的基数,也可以创建 bitmap 索引。

例如,一个一百万行的表,某个列具有 10000 个不重复的值,可以在该列创建 bitmap 索引。该列的 bitmap 索引会比其 B-tree 索引的性能要好,特别是当这个列经常与其他列进行连接时。

B-tree 索引对高基数的数据具有很高的效率:也就是说,数据具有很多可能的值,例如,CUSTOMER_NAME 或 PHONE_NUMBER。在一些情况下,一个 B-tree 索引会比已索引的数据大。适当地使用,bitmap 索引会比相应的 B-tree 索引要小很多。

在 ad hoc 查询和类型的情形下,bitmap 索引可以显著提高查询性能。WHERE 子句中的 AND 和 OR 条件可以被快速解析,这是在把结果 bitmap 转换成 rowid 前,通过在 bitmap 索引上直接执行相应的 Booleam 操作。如果结果的行数较小,那么无需重新排序就能快速得到结果。

原文地址:https://www.cnblogs.com/liuning8023/p/2765422.html