利用Phoenix为HBase创建二级索引
为什么需要Secondary Index
对于Hbase而言,如果想精确地定位到某行记录,唯一的办法是通过rowkey来查询。如果不通过rowkey来查找数据,就必须逐行地比较每一列的值,即全表扫瞄。对于较大的表,全表扫瞄的代价是不可接受的。
但是,很多情况下,需要从多个角度查询数据。例如,在定位某个人的时候,可以通过姓名、身份证号、学籍号等不同的角度来查询,要想把这么多角度的数据都放到rowkey中几乎不可能(业务的灵活性不允许,对rowkey长度的要求也不允许)。
所以,需要secondary index来完成这件事。secondary index的原理很简单,但是如果自己维护的话则会麻烦一些。现在,Phoenix已经提供了对hbase secondary index的支持,下面将说明这样用Phoenix来在HBase中创建二级索引。
配置HBase以支持Secondary Index
在每一个RegionServer的hbase-site.xml中加入如下的属性:
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
<property>
<name>hbase.region.server.rpc.scheduler.factory.class</name>
<value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
<property>
<name>hbase.rpc.controllerfactory.class</name>
<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
</property>
在每一个Master的hbase-site.xml中加入如下的属性:
<property>
<name>hbase.master.loadbalancer.class</name>
<value>org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.phoenix.hbase.index.master.IndexMasterObserver</value>
</property>
Global Indexing v.s. Local Indexing
Global Indexing
Global indexing targets read heavy, low write uses cases. With global indexes, all the performance penalties for indexes occur at write time. We intercept the data table updates on write (DELETE
, UPSERT VALUES
and UPSERT SELECT
), build the index update and then sent any necessary updates to all interested index tables. At read time, Phoenix will select the index table to use that will produce the fastest query time and directly scan it just like any other HBase table. By default, unless hinted, an index will not be used for a query that references a column that isn’t part of the index.
Local Indexing
Local indexing targets write heavy, space constrained use cases. Just like with global indexes, Phoenix will automatically select whether or not to use a local index at query-time. With local indexes, index data and table data co-reside on same server preventing any network overhead during writes. Local indexes can be used even when the query isn’t fully covered (i.e. Phoenix automatically retrieve the columns not in the index through point gets against the data table). Unlike global indexes, all local indexes of a table are stored in a single, separate shared table. At read time when the local index is used, every region must be examined for the data as the exact region location of index data cannot be predetermined. Thus some overhead occurs at read-time.
Examples
为已有的Phoenix表创建secondary index
首先,在Phoenix中创建一个salted table。
create table EXAMPLE (my_pk varchar(50) not null, M.C0 varchar(50), M.C1 varchar(50), M.C2 varchar(50), M.C3 varchar(50), M.C4 varchar(50), M.C5 varchar(50), M.C6 varchar(50), M.C7 varchar(50),M.C8 varchar(50), M.C9 varchar(50) constraint pk primary key (my_pk)) salt_buckets=10;
这里创建的表名为EXAMPLE,有10个列(列名为{M.C0, M.C1, ···, M.C9})。
然后,再将一个CSV文件中的数据通过Bulkload的方式导入到该表中。
该CSV文件中有1000万条记录,在HDFS中的路径为/user/tao/xt-data/data-10m.csv
,则导入的命令为
HADOOP_CLASSPATH=$(hbase classpath) hadoop jar phoenix-4.3.1-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool -libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar --table EXAMPLE --input /user/tao/xt-data/data-10m.csv
由于导入的过程用到了一些其他的类,所以需要通过-libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar
来将相关的Jar包传给这个MapReduce任务。另外,Bulk CSV Data Loading中提到的方法,要求对于Phoenix 4.0+的版本,指定 HADOOP_CLASSPATH=$(hbase mapredcp)
,但是通过实践,发现这样不行,应该用HADOOP_CLASSPATH=$(hbase classpath)
。
在为表EXAMPLE创建secondary index之前,先看看查询一条数据所需的时间:
可以看到,对名为M.C0的列进行按值查询需要7秒多。
现在,为表EXAMPLE的列M.C0创建Index,如下:
create index my_index on example (m.c0);
此时,查看HBase,会发现其中多了一个名为MY_INDEX
的表。而从Phoenix中则看不到该表。
在为EXAMPLE创建了index之后,我们再来进行查询。
这次,查询时间从7秒多降到了0.097秒。
确保Query使用Index
By default, a global index will not be used unless all of the columns referenced in the query are contained in the index.
在上例中,由于我们只对M.C0
创建了索引,所以如果查询项中包含其他列的话(主键MY_PK
除外),是不会使用index的。此外,如果查询项不包含其他列,但是条件查询语句中包含了其他列(主键MY_PK
除外),也会引发全表扫瞄。如下:
要让一个查询使用index,有三种方式:
#1. 创建 convered index;
#2. 在查询中提示其使用index;
#3. 创建 local index
#方式1 - Covered Index
What is a Covered Index?
Using Covering Index to Improve Query Performance
如果在某次查询中,查询项或者查询条件中包含除被索引列之外的列(主键MY_PK
除外)。默认情况下,该查询会触发full table scan,但是使用covered index则可以避免全表扫描。
例如,我们按照EXAMPLE
的方式创建了另一张表EXAMPLE_2
,表中的数据和表的schema与EXAMPLE
都是相同的,不同之处在于EXAMPLE_2
对其M.C1
这一列创建了covered index。
create index my_index_2 on example_2 (m.c0) include (m.c1);
现在,如果查询项中不包含除M.C0
和M.C1
之外的列,而且查询条件不包含除M.C0
之外的列,则可以确保该查询使用Index,如下:
#方式2 - Hint
在select
和column_name
之间加上/*+ Index(<表名> <index名>)*/
,如下:
This will cause each data row to be retrieved when the index is traversed to find the missing M.C1 column value. This hint should only be used if you know that the index has good selective (i.e. a small number of table rows have a value of ‘c0_00000000’ in this example), as otherwise you’ll get better performance by the default behavior of doing a full table scan
#方式3 - Local Index
Unlike global indexes, local indexes will use an index even when all columns referenced in the query are not contained in the index. This is done by default for local indexes because we know that the table and index data coreside on the same region server thus ensuring the lookup is local.
对于HBase 0.98.6的版本,似乎不支持创建local index,如下:
其他方面
Functional Index
Another useful feature that was introduced in the 4.3 release is functional indexes. Functional indexes allow you to create an index not just on columns, but on an arbitrary expressions. Then when a query uses that expression, the index may be used to retrieve the results instead of the data table. For example, you could create an index on UPPER(FIRST_NAME||' '||LAST_NAME)
to allow you to do case insensitive searches on the combined first name and last name of a person.
Phoenix supports two types of indexing techniques: global and local indexing. Each are useful in different scenarios and have their own failure profiles and performance characteristics.
下面为表EXAMPLE
创建一个Functional Index,如下:
create index index_upper_c2 on example (upper(m.c2)) include m.c2
这里,我们实际上为表
EXAMPLE
又创建了一个名为INDEX_UPPER_C2
的Index。也就是说,可以为同一张表创建多个Index。
Index 排序
create index my_index on example (M.C1 desc, M.C0) include (M.C2);
删除Index
drop index `index-name` on `table-name`
索引表属性
create table
和create index
都可以将一些属性传递给对应的HBase表,例如:
CREATE INDEX my_index ON my_table (v2 DESC, v1) INCLUDE (v3)
SALT_BUCKETS=10, DATA_BLOCK_ENCODING='NONE';
对于global indexes,如果primary table是salted table,则index会自动地成为salted index。对于local indexes,则不允许指定salt_buckets
。
Immutable Indexing
For a table in which the data is only written once and never updated in-place, certain optimizations may be made to reduce the write-time overhead for incremental maintenance. This is common with time-series data such as log or event data, where once a row is written, it will never be updated. To take advantage of these optimizations, declare your table as immutable by adding the IMMUTABLE_ROWS=true
property to your DDL statement:
CREATE TABLE my_table (k VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true;
All indexes on a table declared with IMMUTABLE_ROWS=true
are considered immutable (note that by default, tables are considered mutable). For global immutable indexes, the index is maintained entirely on the client-side with the index table being generated as change to the data table occur. Local immutable indexes, on the other hand, are maintained on the server-side. Note that no safeguards are in-place to enforce that a table declared as immutable doesn’t actually mutate data (as that would negate the performance gain achieved). If that was to occur, the index would no longer be in sync with the table.
如果创建的表是immutable table,如下:
create table my_tablek(VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true
那么,为该表创建的所有index都是immutable indexes。
可以将一个已有的immutable table转变为mutable table,可以通过如下命令:
alter table my_table set IMMUTABLE_ROWS=false
问题
#1 能不能为多个column建立index(在同一个index table中)?
例如,如下命令似乎是可以对两个column创建index:
create index my_idx on example(m.c0, m.c1)
但是,似乎只有列m.c0
的真正具有索引,列m.c1
似乎没有索引:
答案是:可以为多个column创建索引,但是在查询时要按照索引列的顺序来查询。例如,为M.C0
、M.C1
和M.C2
建立索引:
create index idx on example (m.c0, m.c1, m.c2)
在查询时,可以同时根据将这3列作为条件,且顺序不限。但是,第一列必须是M.C0
。
这是因为:当为多列建立索引时,rowkey实际上是这些column的组合,并且是按照它们的先后顺序的组合。
如果查询时第一列不是M.C0
,那么就要进行full scan,速度会很慢。而如果查询的第一列是M.C0
,就可以直接将满足关于M.C0
的数据记录找出来。即使后面还有没有被索引的列,也可以很快得到结果,因为满足关于M.C0
的结果集已经不大了(如果是这种情况的话),对其再进行一次查询不会是full scan。
#2 Bulkload的数据的index能不能自动同步?
维护Index的原理:当对data table执行写入操作时,delete
、upsert values
和upsert select
会被捕获,并据此来更新data table所对应的index。
We intercept the data table updates on write (DELETE, UPSERT VALUES and UPSERT SELECT), build the index update and then sent any necessary updates to all interested index tables
当以bulkload的方式来将数据导入到data table时,会绕开HBase的常规写入路径(client –> buffer –> memstore –> HLog –> StoreFile –> HFile),直接生成最终的HFiles。对于bulkload,对data table的更新能不能被捕获,进而自动维护相应index呢?我们来验证。
首先建立一个空的data table:
create table EXAMPLE (PK varchar primary key, M.C0 varchar, M.C1 varchar, M.C2 varchar, M.C3 varchar, M.C4 varchar, M.C5 varchar, M.C6 varchar, M.C7 varchar, M.C8 varchar, M.C9 varchar) salt_buckets = 20
再为其创建2个index:
create index IDX_C0 on EXAMPLE(M.C0);
create index IDX_C1 on EXAMPLE(M.C1);
现在用MapReduce将一个包含了1亿行记录的CSV文件bulkload到数据表EXAMPLE
中去:
sudo -u hbase HADOOP_CLASSPATH=$(hbase classpath) hadoop jar phoenix-4.3.1-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool -libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar --table EXAMPLE --input /user/tao/data/data-100m.csv
从输出来看,一共启动了3个MR任务,分别针对数据表EXAMPLE
、索引表IDX_C0
和索引表IDX_C1
,如下:
现在,再看看查询时间:
所以,一旦index创建之后,不论是否使用bulkload来更新data table,都会保证index的自动更新。
利用Phoenix为HBase创建二级索引的更多相关文章
- Phoneix(三)HBase集成Phoenix创建二级索引
一.Hbase集成Phoneix 1.下载 在官网http://www.apache.org/dyn/closer.lua/phoenix/中选择提供的镜像站点中下载与安装的HBase版本对应的版本. ...
- 通过phoenix在hbase上创建二级索引,Secondary Indexing
环境描述: 操作系统版本:CentOS release 6.5 (Final) 内核版本:2.6.32-431.el6.x86_64 phoenix版本:phoenix-4.10.0 hbase版本: ...
- phoenix连接hbase数据库,创建二级索引报错:Error: org.apache.phoenix.exception.PhoenixIOException: Failed after attempts=36, exceptions: Tue Mar 06 10:32:02 CST 2018, null, java.net.SocketTimeoutException: callTimeou
v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...
- HBase的二级索引,以及phoenix的安装(需再做一次)
一:HBase的二级索引 1.讲解 uid+ts 11111_20161126111111:查询某一uid的某一个时间段内的数据 查询某一时间段内所有用户的数据:按照时间 索引表 rowkey:ts+ ...
- 085 HBase的二级索引,以及phoenix的安装(需再做一次)
一:问题由来 1.举例 有A列与B列,分别是年龄与姓名. 如果想通过年龄查询姓名. 正常的检索是通过rowkey进行检索. 根据年龄查询rowkey,然后根据rowkey进行查找姓名. 这样的效率不高 ...
- HBase建立二级索引的一些解决方式
HBase的一级索引就是rowkey,我们仅仅能通过rowkey进行检索. 假设我们相对hbase里面列族的列列进行一些组合查询.就须要採用HBase的二级索引方案来进行多条件的查询. 常见的二级索引 ...
- Hadoop生态圈-phoenix(HBase)的索引配置
Hadoop生态圈-phoenix(HBase)的索引配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 创建索引是为了优化查询,我们可以在phoenix上配置索引方式. 一.修改 ...
- [How to] MapReduce on HBase ----- 简单二级索引的实现
1.简介 MapReduce计算框架是二代hadoop的YARN一部分,能够提供大数据量的平行批处理.MR只提供了基本的计算方法,之所以能够使用在不用的数据格式上包括HBase表上是因为特定格式上的数 ...
- hbase构建二级索引解决方案
关注公众号:大数据技术派,回复"资料",领取1024G资料. 1 为什么需要二级索引 HBase的一级索引就是rowkey,我们仅仅能通过rowkey进行检索.假设我们相对Hbas ...
随机推荐
- Android无线测试之—UiAutomator UiSelector API介绍之二
Android的布局与组件及组件属性介绍 一.布局: 1)线性布局:控价在线性方向上一次排列 2)表格布局:向表格一样有标准的行和列 3)相对布局:通过相对定位的方式让控件出现在布局的任何位置 4)帧 ...
- iOS Document Interaction 编程指南
本文转载至 http://www.2cto.com/kf/201306/219382.html iOS支持在你的app中用其他app预览和显示文档.iOS还支持文件关联,允许其他app通过你的程序打开 ...
- 《从零开始学Swift》学习笔记(Day 33)——属性观察者
原创文章,欢迎转载.转载请注明:关东升的博客 为了监听属性的变化,Swift提供了属性观察者.属性观察者能够监听存储属性的变化,即便变化前后的值相同,它们也能监听到. 属性观察者主要有以下两个: l ...
- 在Visual Studio 2015的Cordova项目中使用Gulp
之前一直是在vs 2013中使用Cordova来开发移动app(目前有iPad版/iPhone版/安卓版),准备到下一个milestone的时候升级到2015,这两天在尝试各种东西. 2015中的co ...
- Dictionary——通过value找Key
Dictionary<string, string> dic = GetRoleDescriptions(); string key = dic.FirstOrDefault(x => ...
- text files and binary files
https://en.wikipedia.org/wiki/Text_file https://zh.wikipedia.org/wiki/文本文件
- python系列四:Python3字符串
#!/usr/bin/python #Python3 字符串#可以截取字符串的一部分并与其他字段拼接var1 = 'Hello World!'print ("已更新字符串 : ", ...
- python错误笔记
1.print "hello world!";SyntaxError:Missing parentheses in call to ‘paint’ . Did you mean p ...
- AngularCli项目中添加字体图标(Font)详解
本文主要讲如何在AngularCli生成的项目中使用字体图标. 一 SVG图标准备 将需要转换为字体图标的图片转换为SVG格式. 这个让项目视觉设计师搞定即可. 二 SVG图标转Font 可以通过Ic ...
- 随机生成六位验证码函数版(python)
import random def code(n=6,alpha=True): s = '' # 创建字符串变量,存储生成的验证码 for i in range(n): # 通过for循环控制验证码位 ...