一.概述
- Hadoop2.2.0 Hbase0.96.1.1 Phoenix-2.2.0-SNAPSHOT
- Phoenix官网上提供了三个与Hbase兼容的版本,分别为Phoenix2.X-Hbase0.94.X ,Phoenix3.X-Hbase0.94.X,Phoenix4.X-Hbase0.98.1+,没有与Hbase0.96兼容的版本。幸运的是git上有Phoenix Hbase0.96的分支,参考https://github.com/forcedotcom/phoenix/tree/port-0.96。于是我自己编译成了Phoenix-2.2.0-SNAPSHOT 。
二.Phoenix二级索引介绍
- 在Hbase中,按字典顺序排序的rowkey是一级索引。不通过rowkey来查询数据时需要过滤器来扫描整张表。通过二级索引,这样的场景也可以轻松定位到数据。
- Example:
- DDL: create table user (id varchar primary key, firstname varchar, lastname varchar); create index user_idx on user (firstname);
- Query: DDL select id, firstname, lastname from user where firstname = 'foo';
- Phoenix支持两种形式的索引,可变索引和不可变索引。没有一个地方显示的 指定索引是否可变,它们是通过对应的主表是否可变IMMUTABLE_ROWS来默认设定的。
三.不可变索引
- 不可变索引的存储方式是write once, append only。不可变索引使用场景十分有限,仅仅适合于按时间先后顺序的数据(time-series data),此时主表只有新数据进来,不会更新数据。
- 初始化数据
- DDL: create table user (id varchar primary key, firstname varchar, lastname varchar) IMMUTABLE_ROWS=true; create index user_idx on user (firstname)
- DML:upsert into user values(‘ 1’, ‘lh5’, ‘x0b’)
- 此时通过phoenix客户端看到Hbase的数据如下
- 修改数据
- DML:upsert into user values(‘1’, ‘ghz, ‘wmn)
- 此时通过phoenix客户端看到Hbase的数据如下
- 可以看到,主表user的数据已经update,但索引表user_idx的索引并不是修改,而是追加,即write once, append only。
- 新增数据
- DML:upsert into user values(‘2’, ‘ghz, ‘wmn)
- 此时通过phoenix客户端看到Hbase的数据如下
- 可以看到,主表user数据新增了,索引表同时新增了这条记录
四.可变索引
- 通常情况下,主表里的数据并不是time-series data,主表里的数它们随时可能被修改。这个时候,必须使用可变索引来保证索引表伴随着主表的变化一起变化。
- 初始化数据
- DDL: create table user (id varchar primary key, firstname varchar, lastname varchar) ; create index user_idx on user (firstname)
- DML:upsert into user values(‘ 1’, ‘6ws’, ‘ecu’)
- 此时通过phoenix客户端看到Hbase的数据如下
- 修改数据
- 新增数据,对于这两种情况,没有测试成功。在将数据写或更新到主表前,会先将数据写或更新到索引表,就是这里出问题了。目前尚不清楚是我集群环境的问题还是Phoenix本身的问题。先把问题提到了jira https://issues.apache.org/jira/browse/PHOENIX-1051。
五.容错性
- 在upsert操作被成功返回给客户端之前,phoenix保证所有这些数据被写入索引表和主表。对于upsert的每一行数据,phoenix保证要么同时写入到索引表和主表,要么都不更新(all-or-nothing)。
- 有几点需要注意的是
- Phoenix不保证完整的事务,所以你可能看到索引表与主表不一致的情况。如果你想自己同步,可以使用命令alter index user_idx on user rebuild。同时也要注意,建立索引的过程有可能bring down整个hbase集群。
- 对于一行数据来说,写入总是all-or-nothing, written or lost,不会部分写入。
- 更新先写入索引表,再写入主表。
六.优化
你可以在hbase-site.xml里配置以下参数
1. index.builder.threads.max
o 为主表更新操作建立索引的最大线程数
o Default: 10
2. index.builder.threads.keepalivetime
o 上面线程的超时时间
o Default: 60
3. index.writer.threads.max
o 将索引写到索引表的最大线程数
o Default: 10
4. index.writer.threads.keepalivetime
o 上面线程的超时时间
o Default: 60
5. hbase.htable.threads.max
o 同时最多有这么多线程往索引表写入数据
o Default: 2,147,483,647
6. hbase.htable.threads.keepalivetime
o 上面线程的超时时间
o Default: 60
7. index.tablefactory.cache.size
o 缓存10个往索引表写数据的线程
o Default: 10