zoukankan      html  css  js  c++  java
  • Bitmap 索引 vs. Btree 索引:如何选择以及何时使用?——15

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

    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

    本文内容

    • 比较索引
    • 演示环境
    • 步骤 1A(TEST_NORMAL 表 EMPNO 列创建 Bitmap 索引,执行等值查询)
    • 步骤 1B(TEST_NORMAL 表 EMPNO 列创建 B-tree 索引,执行等值查询)
    • 步骤 2A(TEST_RANDOM 表 EMPNO 列创建 Bitmap 索引,执行等值查询)
    • 步骤 2B(TEST_RANDOM 表 EMPNO 列创建 B-tree 索引,执行等值查询)

    一般观点认为,Bitmap 索引更适合具有较低不重复(distinct)值的列——如性别、状态和关系。然而,这个假设并不完全。实际中,Bitmap 索引对系统有个建议,数据不会被很多并发系统频繁更新(不希望数据被系统频繁更新)。事实上,我想说,一个具有 100% 唯一值(unique values)列(该列可以作为主键)上的 Bitmap 索引具有与 B-tree 索引同样的效率。

    本文,我将提供一些例子和执行计划,这些例子有两种索引类型,以及低基数列和高基数列。这些例子将帮助 DBA 理解 Bitmap 索引的使用并不是基数依赖,而是应用依赖。

    另外,本文是基于一篇 2005 年的英文文章,表格数据是原文的,除此之外,是自己在本机测试的结果。

    有意思的是,英文表格中的逻辑 IO 和物理 IO 数据。逻辑 IO 数据与本文测试结果一致,而物理 IO 基本都不一样,英文中大都不为 0,而在测试我的中,几乎都为 0。我的理解是,尽管一百万的数据,但其实总体量很小,而且跟硬件发展有也有很大关系。

    英文原文下载

    比较索引


    在唯一值列上使用 Bitmap 索引有很多不利之处——其中一个就是需要足够的空间(Oracle 也不推荐这样做)。然而,Bitmap 索引的大小依赖索引列的基数和数据分布。因此,GENDER 列的 Bitmap 索引要比其 B-tree 索引小。相反,EMPNO 列(可作为主键列)的 Bitmap 索引要比其 B-tree 索引大很多。但因为,访问决策支持系统(decision-support systems,DSS)的用户比访问事务处理系统(transaction-processing,OLTP)要少很多(毕竟只有高层领导才会用)。对这些应用程序来说,资源不是问题。

    为了说明这个观点,我创建两个表,TEST_NORMAL 和 TEST_RANDOM,插入 1000000 条数据。插入 TEST_NORMAL 表的数据是按顺序的,而 TEST_RANDOM 表的数据是根据 TEST_NORMAL 表数据随机追加的。

    Create table test_normal (empno number(10), ename varchar2(30), sal number(10));
    Begin
      For i in 1 .. 1000000 Loop
        Insert into test_normal
        values
          (i, dbms_random.string('U', 30), dbms_random.value(1000, 7000));
        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;
    SQL> select count(*) "Total Rows" from test_normal;
     
    Total Rows
    ----------
       1000000
     
    SQL> select count(distinct empno) "Distinct Values" from test_normal;
     
    Distinct Values
    ---------------
            1000000
     
    SQL> select count(*) "Total Rows" from test_random;
     
    Total Rows
    ----------
       1000000
     
    SQL> select count(distinct empno) "Distinct Values" from test_random;
     
    Distinct Values
    ---------------
            1000000
     
    SQL> 

    TEST_NORMAL 表的数据是已有组织,而 TEST_RANDOM 表是非组织的,它是根据 TEST_NORMAL 表随机追加的。如下所示,选择最前的两行数据。

    SQL> select * from test_normal where rownum<=2;
     
          EMPNO ENAME                                  SAL
    ----------- ------------------------------ -----------
              1 MNSUXLUHXALBKPRABKEOQLVMMVGBQF        1669
              2 QKCBLPNSRVSEVDIDTMJOIFQTYJZVOQ        6476
     
    SQL> select * from test_random where rownum<=2;
     
          EMPNO ENAME                                  SAL
    ----------- ------------------------------ -----------
         380959 MDWFHKQEVYAUYBLGYPIFBDJDKPPCEZ        4287
         222486 ELOBFRHYVLXZRUVOSMTGBERRVARJSL        3601
     
    SQL> 

    两个表的 EMPNO 列值 100% 不重复,可以作为表主键。如果定义该列为主键,那么你只能为该列创建 B-tree 索引,不能是 Bitmap,因为,Oracle 不支持 Bitmap 主键索引。

    为了分析这些索引的行为,我们将执行如下步骤:

    1,在 TEST_NORMAL 表上

        A - 在 EMPNO 列上创建 Bitmap 索引,执行一些等值查询。

        B - 在 EMPNO 列上创建 B-tree 索引,执行一些等值查询,并比较逻辑 IO 和物理 IO。

    2,在 TEST_RANDOM 表上

        A - 同步骤 1A。

        B - 同步骤 1B。

    3,在 TEST_NORMAL 表上

        A - 同步骤 1A,但执行范围查询

        B - 同步骤 1B,但执行范围查询。然后比较统计信息。

    4,在 TEST_RANDOM 表上

        A - 同步骤 3A。

        B - 同步骤 3B。

    5,在 TEST_NORMAL 表上

        A - 在 SAL 列创建 Bitmap 索引,然后执行等值和范围查询。

        B - 在 SAL 列创建 B-tree 索引,然后执行等值和范围查询(同 5A 一样)。比较这些查询 IO。

    6,向两个表添加 GENDER 列,并基于某个条件用三个可能的值更新:M、F 和 null。

    7,在 GENDER 列创建 Bitmap 索引,然后执行等值查询。

    8,在 GENDER 列创建 B-tree 索引,然后执行等值查询。比较步骤 7 的结果。

    步骤 1 到 4 涉及高基数(100% 不重复)列,而步骤 5 是一个正常基数,步骤 7 和 8 是低基数列。

    演示环境


    • Windows 7 64 位 旗舰版 操作系统
    • Oracle 11g Release 2 (11.2)
    • Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz - 双物理内核 四线程
    • 4G 内存

    步骤 1A(在 TEST_NORMAL)


    该步骤,将在 TEST_NORMAL 表上创建 Bitmap 索引,然后检查索引大小及其聚类系数(clustering factor),和表大小。最后,执行一些等值谓词查询,注意利用 Bitmap 索引时这些查询的 IO 变化。

    SQL> create Bitmap index normal_empno_bmx on test_normal(empno);
     
    Index created
     
    SQL> analyze table test_normal compute statistics for table for all indexes for all indexed columns;
     
    Table analyzed
     
    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_EMPNO_BMX');
     
    SEGMENT_NAME                                                 Size in MB
    ------------------------------------------------------------ ----------
    NORMAL_EMPNO_BMX                                                     28
    TEST_NORMAL                                                          50
     
    SQL> select index_name, clustering_factor from user_indexes;
     
    INDEX_NAME                     CLUSTERING_FACTOR
    ------------------------------ -----------------
    NORMAL_EMPNO_BMX                         1000000
     
    SQL> 

    可以看到,索引大小为 28 MB,聚类系数 1000000 等于表的行数。现在,执行一些等值查询:                              

    SQL> set autot traceonly
    SQL> select * from test_normal where empno=&empno;
    输入 empno 的值:  1000
    原值    1: select * from test_normal where empno=&empno
    新值    1: select * from test_normal where empno=1000
     
     
    执行计划
    ----------------------------------------------------------
    Plan hash value: 4267925254
     
    -------------------------------------------------------------------------------------------------
    | Id  | Operation                    | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT             |                  |     1 |    34 |     3  (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID | TEST_NORMAL      |     1 |    34 |     3  (0)| 00:00:01 |
    |   2 |   BITMAP CONVERSION TO ROWIDS|                  |       |       |           |          |
    |*  3 |    BITMAP INDEX SINGLE VALUE | NORMAL_EMPNO_BMX |       |       |           |          |
    -------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       3 - access("EMPNO"=1000)
     
     
    统计信息
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              5  consistent gets
              0  physical reads
              0  redo size
            695  bytes sent via SQL*Net to client
            519  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
              1  rows processed
     
    SQL>

    步骤 1B(在 TEST_NORMAL)


    该步先删除 Bitmap 索引,在 EMPNO 列上创建 B-tree 索引。同步骤 1A,检查索引大小及其聚类系数,并执行同样操作。比较 1A 和 1B 的 IO。

    SQL> drop index normal_empno_bmx;
     
    索引已删除。
     
    SQL> create index normal_empno_idx on test_normal(empno);
     
    索引已创建。
     
    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_EMPNO_IDX');
     
    SEGMENT_NAME                                                 Size in MB
    ------------------------------------------------------------ ----------
    NORMAL_EMPNO_IDX                                                     18
    TEST_NORMAL                                                          50
     
    SQL> select index_name, clustering_factor from user_indexes;
     
    INDEX_NAME                     CLUSTERING_FACTOR
    ------------------------------ -----------------
    NORMAL_EMPNO_IDX                            6210
     
    SQL>

    B-tree 索引大小为 18 MB,聚类系数 6210,与表行数相差很大。

    显然,EMPNO 列的 B-tree 索引比 Bitmap 索引小。B-tree 索引的聚类系数于表行数相差相当大,但更接近数据块数量。因此,B-tree 索引对范围谓词查询更有效。现在,使用 B-tree 索引执行 1A 的同样查询。

    SQL> set autot traceonly
    SQL> select * from test_normal where empno=&empno;
    输入 empno 的值:  1000
    原值    1: select * from test_normal where empno=&empno
    新值    1: select * from test_normal where empno=1000
     
     
    执行计划
    ----------------------------------------------------------
    Plan hash value: 1781697849
     
    ------------------------------------------------------------------------------------------------
    | Id  | Operation                   | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |                  |     1 |    34 |     4 (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| TEST_NORMAL      |     1 |    34 |     4 (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | NORMAL_EMPNO_IDX |     1 |       |     3 (0)| 00:00:01 |
    ------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       2 - access("EMPNO"=1000)
     
     
    统计信息
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              5  consistent gets
              0  physical reads
              0  redo size
            695  bytes sent via SQL*Net to client
            519  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
              1  rows processed
     
    SQL>

    正如你看到的,当执行不同的 EMPNO 值查询时,consistent gets 和 physical reads 值对 Bitmap 索引和 B-tree 索引在 100% 不重复列上是一致的。

    表 1 TEST_NORMAL 表 EMPNO 列 Bitmap 与 B-tree 索引统计信息比较

      Bitmap B-tree
    size 28MB 18MB
    clustering factor 1000000 6210
    blocks    

    表 2 TEST_NORMAL 表 EMPNO 列 Bitmap 与 B-tree 索引等值查询比较

    Bitmap

    B-tree

    consistent gets

    physical reads

    EMPNO

    consistent gets

    physical reads

    5   

    0

    1000

    5

    0

    5

    2

    2398

    5

    2

    5

    2

    8545

    5

    2

    5

    2

    98008

    5

    2

    5

    2

    85342

    5

    2

    5

    2

    128444

    5

    2

    5

    2

    858

    5

    2

    步骤 2A(在 TEST_RANDOM)


    现在,在 TEST_RANDOM 表上进行同 1A 的实验——创建 Bitmap 索引,执行等值查询:

    SQL> create bitmap index random_empno_bmx on test_random(empno);
     
    索引已创建。
     
    SQL> analyze table test_random 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_RANDOM','RANDOM_EMPNO_BMX');
     
    SEGMENT_NAME                                                 Size in MB
    ------------------------------------------------------------ ----------
    RANDOM_EMPNO_BMX                                                     28
    TEST_RANDOM                                                          50
     
    SQL> select index_name, clustering_factor from user_indexes;
     
    INDEX_NAME                     CLUSTERING_FACTOR
    ------------------------------ -----------------
    RANDOM_EMPNO_BMX                         1000000
    NORMAL_EMPNO_IDX                            6210
     
    SQL>

    Bitmap 索引统计信息(索引大小 28M 及其聚类系数 1000000)同 TEST_NORMAL 表索引一致。

    SQL> set autot traceonly
    SQL> select * from test_random where empno=&empno;
    输入 empno 的值:  1000
    原值    1: select * from test_random where empno=&empno
    新值    1: select * from test_random where empno=1000
     
     
    执行计划
    ----------------------------------------------------------
    Plan hash value: 3133668422
     
    -------------------------------------------------------------------------------------------------
    | Id  | Operation                    | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT             |                  |     1 |    39 |     3  (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID | TEST_RANDOM      |     1 |    39 |     3  (0)| 00:00:01 |
    |   2 |   BITMAP CONVERSION TO ROWIDS|                  |       |       |           |          |
    |*  3 |    BITMAP INDEX SINGLE VALUE | RANDOM_EMPNO_BMX |       |       |           |          |
    -------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       3 - access("EMPNO"=1000)
     
     
    统计信息
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              5  consistent gets
              0  physical reads
              0  redo size
            695  bytes sent via SQL*Net to client
            519  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
              1  rows processed
     
    SQL>

    步骤 2B(在 TEST_RANDOM)


    与步骤 1B 相比,我们现在删除 bitmap 索引,在 EMPNO 列上创建 B-tree 索引。

    SQL> drop index RANDOM_EMPNO_BMX;
     
    索引已删除。
     
    SQL> create index random_empno_idx on test_random(empno);
     
    索引已创建。
     
    SQL> analyze table test_random 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_RANDOM','RANDOM_EMPNO_IDX');
     
    SEGMENT_NAME                                                 Size in MB
    ------------------------------------------------------------ ----------
    RANDOM_EMPNO_IDX                                                     18
    TEST_RANDOM                                                          50
     
    SQL> select index_name, clustering_factor from user_indexes;
     
    INDEX_NAME                     CLUSTERING_FACTOR
    ------------------------------ -----------------
    RANDOM_EMPNO_IDX                          999823
    NORMAL_EMPNO_IDX                            6210
     
    SQL>

    索引大小等于 TEST_NORMAL 表上的索引大小,但是聚类系数很大,接近表行数,这使得该索引对范围谓词查询效率不高(正如步骤 4 看到的)。聚类系数不会影响等值谓词查询,因为数据行具有 100% 不重复值,每个键的行数为 1。

    现在执行等值谓词查询。

    SQL> set autot traceonly
    SQL> select * from test_random where empno=&empno;
    输入 empno 的值:  1000
    原值    1: select * from test_random where empno=&empno
    新值    1: select * from test_random where empno=1000
     
     
    执行计划
    ----------------------------------------------------------
    Plan hash value: 3101594612
     
    ------------------------------------------------------------------------------------------------
    | Id  | Operation                   | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |                  |     1 |    39 |     4 (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| TEST_RANDOM      |     1 |    39 |     4 (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | RANDOM_EMPNO_IDX |     1 |       |     3 (0)| 00:00:01 |
    ------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       2 - access("EMPNO"=1000)
     
     
    统计信息
    ----------------------------------------------------------
              1  recursive calls
              0  db block gets
              5  consistent gets
              0  physical reads
              0  redo size
            695  bytes sent via SQL*Net to client
            519  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
              1  rows processed
     
    SQL>

    另外,结果与步骤 1A 和 1B 一致。数据的分布对唯一列不会影响 consistent gets 和 physical reads。

    聚类系数(clustering factor) - 基于索引值,指示表中行顺序的量。

    • 若该值接近块数量,则表非常有序。这种情况下,单个叶子块中的索引条目趋于指向相同数据块中的行。
    • 若该值接近行数,则表非常随机。这种情况下,单个叶子块中的索引条目趋于指向相同数据块中的行,是不可能的。

    该值对于 Bitmap 索引不适用,或不可用。可以从表 3 看出来。

    表 3 TEST_NORMAL 和 TEST_RANDOM 表 EMPNO 列索引统计信息比较

    B-tree

    TEST_NORMAL

    TEST_RANDOM

    |

    Bitmap

    TEST_NORMAL

    TEST_RANDOM

    size

    18MB

    18MB

    |

    size

    28MB

    28MB

    clustering factor

    6210

    999823

    |

    clustering factor

    1000000

    1000000

    http://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_1096.htm#i1578369

  • 相关阅读:
    angularJs实现星星等级评分
    angular自定义过滤器在页面和控制器中的使用
    字符串单个替换
    angular中的$cookies和$cookieStore设置过期时间
    DOM操作和jQuery实现选项移动操作
    PHP 中英文混排截取字符串
    固定格式时间转换时间戳
    PHP生成唯一订单号的方法汇总
    MySQL优化索引及优化汉字模糊查询语句
    简单封装数据库类
  • 原文地址:https://www.cnblogs.com/liuning8023/p/2765416.html
Copyright © 2011-2022 走看看