官网:http://phoenix.apache.org/secondary_indexing.html
测试环境:phoenix4.14.1 hbase1.1.1
一、索引配置
建立非事务性、可变索引表需要在hbase-site.xml添加如下配置:
hbase.regionserver.wal.codec
org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec
hbase.region.server.rpc.scheduler.factory.class
org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory
Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates
hbase.rpc.controllerfactory.class
org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory
Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates
注意:
若hive整合了hbase,需要在hive的lib中添加phoenix-core-x.x.x-HBase-x.x.jar,否则在创建hive映射hbase的表时报如下错:
Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory
在phoenix4.8以下还需添加如下配置:
hbase.master.loadbalancer.class
org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer
hbase.coprocessor.master.classes
org.apache.phoenix.hbase.index.master.IndexMasterObserver
hbase.coprocessor.regionserver.classes
org.apache.hadoop.hbase.regionserver.LocalIndexMerger
二、索引介绍
- 全局索引
全局索引为默认索引类型。适用于大量读少量写的场景,由于全局索引会拦截(DELETE, UPSERT VALUES and UPSERT SELECT)数据更新并更新索引表,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。
CREATE INDEX my_index ON my_table (v1,v2)
注意:
1、假设数据表tb1(A pk,B,C,D,E) 创建全局索引表tb_idx(B,C),若查询列(select B,C,D)包含非索引列D则该查询不会走索引表。可以建立覆盖索引tb_idx(B,C)include(D)或 使用本地索引 或 用Hint 强制指定走索引,eg:SELECT /*+ INDEX(my_table my_index) */ v2 FROM my_table WHERE v1 = 'foo'
2、本段摘自:https://m.aliyun.com/yunqi/articles/633486?spm=a2c4e.11155435.0.0.d6644db40AFswl
创建如下组合索引。之前我们已经提到索引表中的Row key是字典序存储的,什么样的查询适合这样的索引结构呢?
CREATE INDEX B_C_D_IDX ON DATA_TABLE(B,C,D);
所有字段条件以=操作符为例:
注:上表查询中and条件不一定要和索引组合字段顺序一致,可以任意组合。
在实际使用中我们也只推荐使用1 ~ 4,遵循前缀匹配原则,避免触发扫全表。5 ~ 7条件就要扫描全表数据才能过滤出来符合这些条件的数据,所以是极力不推荐的。其它
- 对于order by字段或者group by字段仍然能够使用二级索引字段来加速查询。
- 尽量通过合理的设计数据表的主键规避建更多的索引表,因为索引表越多写放大越严重。
- 使用了ROW_TIMESTAMP特性后不能使用全局索引
- 对索引表适当是的使用加盐特性能提升查询写入性能,避免热点。
- 本地索引
本地索引适用于少读多写的场景,因为本地索引的索引数据和表数据共同驻留在同一服务器上,防止写操作期间出现任何网络开销。与全局索引不同,即使查询中引用的所有列不在索引中也本地索引将使用索引表。因为默认情况下,表和索引数据位于同一区域服务器的核心,因此可以确保查找是本地的。
CREATE LOCAL INDEX my_index ON my_table (v1)
- 覆盖索引
覆盖索引是基于全局索引和本地索引的扩展。创建索引时将查询需要返回的列包含(include),此时被包含的列也会被写进索引表。索引列和原rowkey拼接作为索引表rowkey,被包含的列做为索引表的列,列簇名和原表相同。查询数据时匹配到索引表后直接返回数据,不用再去查询原表。
CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3)
存储结构示例:
- 函数索引
函数索引也是基于全局索引和本地索引的扩展。基于Phoenix封装的函数(function)表达式构建索引,以改函数表达式结果和原rowkey拼接作为索引表rowkey。
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))
# 查询使用
SELECT EMP_ID FROM EMP WHERE UPPER(FIRST_NAME||' '||LAST_NAME)='JOHN DOE'
存储结构示例:
三、索引状态
索引表状态有如下几种,在SYSTEM.CATALOG
表中可以查看,或者使用!tables
命令时可以查看到。
状态名 | 描述 |
---|---|
DISABLE ("x") | 索引将处于不可用的维护状态,同时将不能用于查询中。 |
REBUILD ("r") | 索引将完成重建,同时一旦重建完成此索引将能被在此用于查询中。 |
BUILDING ("b") | 将从索引不可用的时间戳处重建索引直到重建完成。 |
INACTIVE ("i") / UNUSABLE ("d") | 索引不能用于查询中,但索引仍然在不可用的维护状态。 |
ACTIVE ("a")/USABLE ("e") | 索引表能被正常使用于查询中。 |
PENDING_ACTIVE | (phoenix 4.11+)索引处于自动rebuild状态中,即将转换为active状态。 |
PENDING_DISABLE | (phoenix 4.11+)写索引失败,正在进行重试操作,即将转换为disable状态。 |
注意:必须维持索引表处于active状态,才能确保索引数据的完整一致。
四、异步索引
默认情况下创建的索引表是同步的,在数据量大的情况下是不可行的。从4.5开始,索引的初始填充可以通过在索引创建DDL语句中包含ASYNC关键字来异步完成。必须通过以下两步才能完成异步索引的构建。
第一步:
CREATE INDEX async_index ON my_schema.my_table (v) ASYNC
另外,填充索引表的map reduce作业必须通过HBase命令行单独启动。只有当map reduce作业完成时,索引才会被激活并开始在查询中使用。作业对于客户端退出是有弹性的可恢复的。output-path选项用于指定用于将HFiles写入的HDFS目录。
第二步:
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool
--schema MY_SCHEMA --data-table MY_TABLE --index-table ASYNC_IDX
--output-path ASYNC_IDX_HFILES
实例:
hbase org.apache.phoenix.mapreduce.index.IndexTool --schema TEST2 --data-table SIMUL_DAY_ELEC_OUT --index-table SIMUL_DAY_ELEC_OUT_INDEX --output-path ASYNC_IDX_HFILES
Build同步全局索引,如数据量较大需要在客户端配置如下超时参数,防止因为链接超时中断:
1、 phoenix.query.timeoutMS
2、 hbase.rpc.timeout
3、 hbase.client.scanner.timeout.period
或者使用异步索引。
异步索引表遇到的一些问题
五、不可变索引
对于数据只写入一次且从未就地更新的表,可以建立非可变表进行优化,以减少增量维护的写入时开销。
通过IMMUTABLE_ROWS=true
属性开启不可变表。
CREATE TABLE my_table (k VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true
修改不可变表为可变表
ALTER TABLE my_table SET IMMUTABLE_ROWS=false
使用IMMUTABLE_ROWS=true
声明的表上的所有索引都被认为是不可变的(注意,默认情况下,表被认为是可变的)。对于全局不可变索引,索引完全在客户端上维护,当数据表发生更改时生成索引表。另一方面,本地不可变索引是在服务器端维护的。请注意,没有适当的保护措施来强制声明为不可变的表实际上不会改变数据(因为这会抵消所获得的性能收益)。如果不可变的表发生数据更改,索引将不再同步。、
总结:Mutable表的索引同步是在server端完成,适用于索引强一致要求,索引数据有更新的业务需求。Immutable表索引同步在客户端维护,索引数据无更新,适用于时序、日志数据场景。
六、索引一致性
对于非事务性可变表,我们通过将索引更新添加到主表行的Write-Ahead-Log (WAL)条目来维护索引更新的持久性。只有在成功地将WAL条目同步到磁盘之后,我们才尝试更新索引/主表。默认情况下,我们并行编写索引更新,这导致了非常高的吞吐量。如果在编写索引更新时服务器崩溃,我们将在WAL恢复过程中将所有索引更新重放到索引表中,并依赖于更新的幂等性来确保正确性。因此,非事务性可变表上的索引只在主表后面进行过一次批量编辑。(前提:开启WAL)
三种级别一致性保证方式
a、禁止表写,直到可变索引是一致的(最高级别)
更新索引失败的情况下,应该暂时禁用对数据表的写入直到索引恢复联机并与数据表同步为止。索引将保持活动状态,并像往常一样继续供查询使用。在phoenix服务端修改如下配置:
phoenix.index.failure.block.write
必须为true,才能在提交失败时使对数据表的写操作失败,直到索引能够跟上数据表为止。
phoenix.index.failure.handling.rebuild
必须为true(默认值),才能在提交失败时在后台重新构建可变索引。
b、在写入失败时禁用可变索引,直到一致性恢复(中等级别)
在phoenix服务端修改如下配置:
phoenix.index.failure.handling.rebuild
必须为true(默认值),才能在提交失败时在后台重新构建可变索引。
phoenix.index.failure.handling.rebuild.interval
控制服务器检查是否需要部分重新构建可变索引以赶上对数据表的更新的毫秒频率。默认值是10000或10秒。
phoenix.index.failure.handling.rebuild.overlap.time
控制从发生故障的时间戳返回到执行部分重建时返回的毫秒数。默认值是1。
c、在写失败时禁用可变索引,手动重新构建(最低级别)
当对辅助索引的写入失败时,索引将被标记为禁用,需要手动重新构建索引以使查询能够再次使用该索引。
phoenix.index.failure.handling.rebuild
必须设置为false,以便在提交失败时禁用在后台重建可变索引。
七、索引常见问题及建议
什么时候删除重建disable状态的索引比直接rebuild更快
当查看SYSTEM.CATALOG中索引表对应的INDEX_DISABLE_TIMESTAMP字段值为0时直接重建。对phoenix映射的hbase表创建索引,直接使用hbase API写Hbase表,索引不会同步
只有通过phoenix语法写入才能使用二级索引同步机制。不建议手动读写索引表,容易造成索引数据不一致。使用同步索引表快还是使用MR的异步填充索引快
如果数据量在10亿以内,并且RS配置相对较高,推荐使用同步方式,但集群负载会较高。异步方式对HBASE集群影响较小,因为可以直接写hdfs。怎么避免发生 ERROR 2008:Unableto find cached index metadata
核心原因是客户端写压力大,建议减小batch和并发数。或在server端调phoenix.coprocessor.maxServerCacheTimeToLiveMs
和phoenix.coprocessor.maxMetadataCacheTimeToLiveMs
两个参数为什么索引状态经常不可用或频繁发生ERROR 1120:Writes to table blocked until index can be updated异常
检查集群状态是否稳定,GC情况是否正常,负载压力是否正常等索引表一直处于
BUILDING
状态
可能是索引填充时存在非法数据(如时间戳类型的值偶尔会遇见这种情况)或其他造成索引填充失败的原因存在。