zoukankan      html  css  js  c++  java
  • [Phoenix] 五、二级索引

    摘要: 目前HBASE只有基于字典序的主键索引,对于非主键过滤条件的查询都会变成扫全表操作,为了解决这个问题Phoenix引入了二级索引功能。然而此二级索引又有别于传统关系型数据库的二级索引,本文将详细描述Phoenix中二级索引功能、用法和原理,希望能够对大家在业务技术选型时起到一些帮助作用。

    一、概要

    目前HBASE只有基于字典序的主键索引,对于非主键过滤条件的查询都会变成扫全表操作,为了解决这个问题Phoenix引入了二级索引功能。然而此二级索引又有别于传统关系型数据库的二级索引,本文将详细描述Phoenix中二级索引功能、用法和原理,希望能够对大家在业务技术选型时起到一些帮助作用。


    二、二级索引

    示例表如下(为了能够容易通过HBASE SHELL对照表内容,我们对属性值COLUMN_ENCODED_BYTES设置为0,不对column family进行编码):

    CREATE TABLE  TEST (
       ID VARCHAR NOT NULL  PRIMARY KEY,
       COL1 VARCHAR,
       COL2 VARCHAR
      ) COLUMN_ENCODED_BYTES=0;
    upsert into TEST values('1', '2', '3');

    1. 全局索引

    全局索引更多的应用在读较多的场景。它对应一张独立的HBASE表。对于全局索引,在查询中检索的列如果不在索引表中,默认的索引表将不会被使用,除非使用hint。

    创建全局索引:

    CREATE INDEX IDX_COL1 ON TEST(COL1)

    通过HBASE SHELL观察生成的索引表IDX_COL1。我们发现全局索引表的RowKey存储了索引列的值和原表RowKey的值,这样编码更有利于提高查询的性能。

    hbase(main):001:0> scan 'IDX_COL1'
    ROW                        COLUMN+CELL
     2x001                    column=0:_0, timestamp=1520935113031, value=x
    1 row(s) in 0.1650 seconds

    实际上全局索引的RowKey将会按照如下格式进行编码。
    Screen_Shot_2018_03_13_at_18_04_32

    • SALT BYTE: 全局索引表和普通phoenix表一样,可以在创建索引时指定SALT_BUCKETS或者split key。此byte正是存储着salt。
    • TENANT_ID: 当前数据对应的多租户ID。
    • INDEX VALUE: 索引数据。
    • PK VALUE: 原表的RowKey。

    2. 本地索引

    因为本地索引和原数据是存储在同一个表中的,所以更适合写多的场景。对于本地索引,查询中无论是否指定hint或者是查询的列是否都在索引表中,都会使用索引表。

    创建本地索引:

    create local index LOCAL_IDX_COL1 ON TEST(COL1);

    通过HBASE SHELL观察表'TEST', 我们可以看到表中多了一行column为L#0:_0的索引数据。

    hbase(main):001:0> scan 'TEST'
    ROW                        COLUMN+CELL
     x00x002x001            column=L#0:_0, timestamp=1520935997600, value=_0
     1                         column=0:COL1, timestamp=1520935997600, value=2
     1                         column=0:COL2, timestamp=1520935997600, value=3
     1                         column=0:_0, timestamp=1520935997600, value=x
    2 row(s) in 0.1680 seconds

    本地索引的RowKey将会按照如下格式进行编码:
    Screen_Shot_2018_03_13_at_20_16_24

    • REGION START KEY : 当前row所在region的start key。加上这个start key的好处是,可以让索引数据和原数据尽量在同一个region, 减小IO,提升性能。
    • INDEX ID : 每个ID对应不同的索引表。
    • TENANT ID :当前数据对应的多租户ID。
    • INDEX VALUE: 索引数据。
    • PK VALUE: 原表的RowKey。

    3. 覆盖索引

    覆盖索引的特点是把原数据存储在索引数据表中,这样在查询到索引数据时就不需要再次返回到原表查询,可以直接拿到查询结果。

    创建覆盖索引:

    create  index IDX_COL1_COVER_COL2 on TEST(COL1) include(COL2);

    通过HBASE SHELL 查询表IDX_COL1_COVER_COL2, 我们发现include的列的值被写入到了value中。

    hbase(main):003:0> scan 'IDX_COL1_COVER_COL2'
    ROW                   COLUMN+CELL
     2x001               column=0:0:COL2, timestamp=1520943893821, value=3
     2x001               column=0:_0, timestamp=1520943893821, value=x
    1 row(s) in 0.0180 seconds

    对于类似select col2 from TEST where COL1='2'的查询,查询一次索引表就能获得结果。其查询计划如下:

    +--------------------------------------------------------------------------------------+-----------------+----------------+---+
    |                                         PLAN                                         | EST_BYTES_READ  | EST_ROWS_READ  | E |
    +--------------------------------------------------------------------------------------+-----------------+----------------+---+
    | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER IDX_COL1_COVER_COL2 ['2']  | null            | null           | n |
    +--------------------------------------------------------------------------------------+-----------------+----------------+---+

    4. 函数索引

    函数索引的特点是能根据表达式创建索引,适用于对查询表,过滤条件是表达式的表创建索引。例如:

    //创建函数索引
    CREATE INDEX CONCATE_IDX ON TEST (UPPER(COL1||COL2))
    
    //查询函数索引
    SELECT * FROM TEST WHERE UPPER(COL1||COL2)='23'

    三、什么是Phoenix的二级索引?

    Phoenix的二级索引我们基本上已经介绍过了,我们回过头来继续看Phoenix二级索引的官方定义:Secondary indexes are an orthogonal way to access data from its primary access path。简单理解为,在主访问路径(通过row key访问)上发生正交的一种方法,更清楚的应该描述为:索引列访问和row key访问产生交集时的一种索引方法。我们来通过一个例子说明:

    1. 对表TESTCOL1创建全局索引
    CREATE INDEX IDX_COL1 ON TEST(COL1);
    2. 对于如下查询必将发生FULL SCAN。
    select * from TEST where COL1='2';

    以上查询的查询计划如下:

    +----------------------------------------------------------------+-----------------+----------------+--------------+
    |                              PLAN                              | EST_BYTES_READ  | EST_ROWS_READ  | EST_INFO_TS  |
    +----------------------------------------------------------------+-----------------+----------------+--------------+
    | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER TEST  | null            | null           | null         |
    |     SERVER FILTER BY COL1 = '2'                                | null            | null           | null         |
    +----------------------------------------------------------------+-----------------+----------------+--------------+
    3. 对于以下查询将会形成点查。因为二级索引是RowKey的交集。
    select * from TEST where id='1' and COL1='2'

    查询计划如下

    +---------------------------------------------------------------------------------------------+-----------------+-------------+
    |                                            PLAN                                             | EST_BYTES_READ  | EST_ROWS_RE |
    +---------------------------------------------------------------------------------------------+-----------------+-------------+
    | CLIENT 1-CHUNK 1 ROWS 203 BYTES PARALLEL 1-WAY ROUND ROBIN POINT LOOKUP ON 1 KEY OVER TEST  | 203             | 1           |
    |     SERVER FILTER BY COL1 = '2'                                                             | 203             | 1           |
    +---------------------------------------------------------------------------------------------+-----------------+-------------+

    对于2中所描述的查询为什么会发生FULL SCAN? 正如Phoenix二级索引官方定义的一样,因为“没有和RowKey列的查询发生正交关系”,除非使用Hint强制指定索引表。


    四、索引Building

    Phoenix的二级索引创建有同步和异步两种方式。

    1. 在执行CREATE INDEX IDX_COL1 ON TEST(COL1)时会进行索引数据的同步。此方法适用于数据量较小的情况。
    2. 异步build索引需要借助MR,创建异步索引语法和同步索引相差一个关键字:ASYNC
    //创建异步索引
    CREATE INDEX ASYNC_IDX ON DB.TEST (COL1) ASYNC
    //build 索引数据
    ${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool --schema DB --data-table TEST --index-table ASYNC_IDX  --output-path ASYNC_IDX_HFILES

    五、索引问题汇总

    1. 创建同步索引超时怎么办?

    在客户端配置文件hbase-site.xml中,把超时参数设置大一些,足够build索引数据的时间。

    <property>
        <name>hbase.rpc.timeout</name>
        <value>60000000</value>
    </property>
    <property>
        <name>hbase.client.scanner.timeout.period</name>
        <value>60000000</value>
    </property>
    <property>
        <name>phoenix.query.timeoutMs</name>
        <value>60000000</value>
    </property>

    2. 索引表最多可以创建多少个?

    建议不超过10个

    3. 为什么索引表多了,单条写入会变慢?

    索引表越多写放大越严重。写放大情况可以参考下图。

    Screen_Shot_2018_03_13_at_21_36_26

    References

    转自:https://yq.aliyun.com/articles/536850


    交流

    如果大家对HBase有兴趣,致力于使用HBase解决实际的问题,欢迎加入Hbase技术社区群交流:

    微信HBase技术社区群,假如微信群加不了,可以加秘书微信: SH_425 ,然后邀请您。

     

     

    ​  钉钉HBase技术社区群

  • 相关阅读:
    Python_字典
    Python_字符串方法
    跳转到新页面,加载过程中加入等待过渡的动态效果
    web项目引入第三方jar包,编译时找不到的问题与及解决方案
    Eclipse的debug按钮介绍
    什么是TCP粘包?怎么解决TCP粘包问题?UDP协议存在粘包问题吗?
    使用Java编写TCP协议发送和接收数据接口
    UUID生成唯一的16位随机数
    如何在父页面中操作/获取iframe页面中的元素?这个小问题折腾了我快半个小时,所以记下来吧!
    如何接收APP的请求,并且如何以json字符串的格式封装响应的数据,然后发送回APP
  • 原文地址:https://www.cnblogs.com/hbase-community/p/8795176.html
Copyright © 2011-2022 走看看