[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
 2\x001                    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
 \x00\x002\x001            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
 2\x001               column=0:0:COL2, timestamp=1520943893821, value=3
 2\x001               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。通过以下例子我们再理解下这个定义。

  1. 对表TESTCOL1创建全局索引
CREATE INDEX IDX_COL1 ON TEST(COL1);
  1. 查询所有字段。
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         |
+----------------------------------------------------------------+-----------------+----------------+--------------+
  1. 查询id字段:
select id from TEST where  COL1='2';

查询计划如下

+---------------------------------------------------------------------------+-----------------+----------------+--------------+
|                                   PLAN                                    | EST_BYTES_READ  | EST_ROWS_READ  | EST_INFO_TS  |
+---------------------------------------------------------------------------+-----------------+----------------+--------------+
| CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER IDX_COL1 ['2']  | null            | null           | null         |
|     SERVER FILTER BY FIRST KEY ONLY                                       | null            | null           | null         |
+---------------------------------------------------------------------------+-----------------+----------------+--------------+

两个查询都没有通过hint强制指定索引表,查询计划显示,查询所有字段时发生了需要极力避免的扫全表操作(一般数据量在几十万级别的扫全表很容易造成集群不稳定),而查询id时利用索引表走了点查。从现象来看,当查询中出现的字段都在索引表中时(可以是索引字段或者数据表主键,也可以是覆盖索引字段),会自动走索引表,否则查询会退化为全表扫描。

在我们实际应用中一个数据表会有多个索引表,为了能让我们的查询使用合理的索引表,目前都需要通过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

相关实践学习
lindorm多模间数据无缝流转
展现了Lindorm多模融合能力——用kafka API写入,无缝流转在各引擎内进行数据存储和计算的实验。
云数据库HBase版使用教程
&nbsp; 相关的阿里云产品:云数据库 HBase 版 面向大数据领域的一站式NoSQL服务,100%兼容开源HBase并深度扩展,支持海量数据下的实时存储、高并发吞吐、轻SQL分析、全文检索、时序时空查询等能力,是风控、推荐、广告、物联网、车联网、Feeds流、数据大屏等场景首选数据库,是为淘宝、支付宝、菜鸟等众多阿里核心业务提供关键支撑的数据库。 了解产品详情:&nbsp;https://cn.aliyun.com/product/hbase &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
6月前
|
SQL 分布式数据库 HIVE
Hbase二级索引_Hive on Hbase 及phoenix详解
Hbase二级索引_Hive on Hbase 及phoenix详解
74 0
|
SQL 分布式数据库 Apache
|
存储 SQL 关系型数据库
MySQL学习笔记-主键索引和二级索引
MySQL学习笔记-主键索引和二级索引
85 0
|
分布式数据库 Hbase
Hbase的表查询操作
Hbase的表查询操作
218 0
|
存储 数据库 索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
【clickhouse】ClickHouse从入门到放弃-二级索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
|
SQL 搜索推荐 Java
「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践
「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践
539 0
「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践
|
SQL 存储 关系型数据库
MySQL-聚集索引与二级索引
聚集索引 二级索引 InnoDB MySQL
214 0
|
分布式计算 Hadoop 分布式数据库
phoenix-5.0.0与CDH6.0.1兼容性导致二级索引不可用问题解决
今天在测试phoenix的二级索引功能时,创建完索引写入数据,出现以下异常: Caused by: org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException: Failed 1 action: org.
1633 0
|
SQL 运维 Java
数据查询的玄铁剑:阿里云HBase二级索引功能解析
HBase原生提供了主键索引,用户可以根据rowkey进行高效的单行读、前缀匹配、范围查询操作。但若需要使用属性列进行查询时,则只能使用filter在查询范围内进行逐行过滤。在扫描范围较大时,会浪费大量的IO,请求RT也无法保证。为此,HBase增强版推出了原生二级索引来解决非rowkey查询的性能问题。
2245 0
数据查询的玄铁剑:阿里云HBase二级索引功能解析
cassandra主键索引介绍
主键索引格式如下: summery.db:index文件的索引,对partition做sampling索引,加快查找。 index.db:索引文件,data文件中每个partition都会有一个索引项,指向该partition在data文件中的偏移,还有对clustering的采样(sampling)索引,可直接指向具体clustering数据偏移。
12316 0