Hbase之phoenix二级索引

二级索引这个特性应该是大部分用户引入Phoenix主要考虑的因素之一。HBase因其历史原因只支持rowkey索引,当使用rowkey来查询数据时可以很快定位到数据位置。现实中,业务查询需求条件往往比较复杂,带有多个查询字段组合,如果用HBase查的话,只能全表扫描进行过滤,效率很低。而Phoenix支持除rowkey外的其它字段的索引创建,即二级索引,查询效率可大幅提升。

二级索引分类

二级索引分为全局索引和本地索引。新建索引实际上是创建了另一张 HBase 表,并把索引字段数据存进去,在查询的时候优先查询索引表数据从而避免了全表扫描。

Global Indexing,即全局索引,适用于读多写少的业务场景。
create index USER_USER_NAME_INDEX on USER ("name");

使用 Global Indexing 在写数据的时候开销很大,因为所有对数据表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),都会引起索引表的更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。

在读数据的时候 Phoenix 会选择优先查询索引表来降低查询消耗的时间。在默认情况下如果待查询的字段不是索引字段的话,索引表是不会被使用的,也就是说不会带来查询速度上的提升。

Local Indexing,即本地索引,适用于写操作频繁以及空间受限制的场景。
create local index USER_USER_NAME_INDEX on USER ("name");

与 Global Indexing 一样,Phoenix 会自动判定在进行查询的时候是否使用索引。使用 Local indexing 时,索引数据和数据表的数据存放在相同的服务器中,这样避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。使用 Local indexing 的时候即使查询的字段不是索引字段索引表也会被使用,这会带来查询速度的提升,这点跟 Global indexing 不同。对于 Local Indexing,一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。

Immutable Index,不可变索引,适用于数据只增加不更新并且按照时间先后顺序存储(time-series data)的场景,如保存日志数据或者事件数据等。

不可变索引的存储方式是 write one,append only。当在 Phoenix 使用 create table 语句时指定 IMMUTABLE_ROWS = true 表示该表上创建的索引将被设置为不可变索引。Phoenix 默认情况下如果在 create table 时不指定 IMMUTABLE_ROWS = true 时,表示该表为 mutable。不可变索引分为 Global immutable index 和 Local immutable index 两种。

mutable index,可变索引,适用于数据有增删改的场景。

Phoenix 默认情况创建的索引都是可变索引,除非在 create table 的时候显式地指定 IMMUTABLE_ROWS = true。可变索引同样分为 Global mutable index 和 Local mutable index 两种。

新建覆盖索引

创建 converted index。如果在某次查询中,查询项或者查询条件中包含有被索引列之外的列(主键除外)。默认情况下,该查询就会触发 full table scan(全表扫描),但是使用 covered index 则可以避免全表扫描。 创建包含某个字段的覆盖索引,创建方式如下:

create index USER_NAME_AGE_INDEX on USER ("name") include("age");
异步创建索引

如果创建索引时原表已经有大量数据了,可能会等很长时间,这时可以使用异步创建的方式。

create index USER_NAME_AGE_INDEX on USER ("name") include("age") ASYNC;

再用 hbase 命令触发执行

hbase org.apache.phoenix.mapreduce.index.IndexTool \
    --data-table=Product \
    --index-table=INDEX_PRODUCT \
    --output-path=/user/spark/ASYNC_INDEX_HFILES     <-- 必须先在 hdfs 创建这个目录

上述命令运行成功后,IndexTool 的 MR 任务会帮助我们构建好索引数据,同时会把索引表的当前状态置为 ACTIVE,然后就可以使用这个索引表进行数据查询了。

强制使用索引

查询的时候提示其使用索引。 在 select 和 column_name 之间加上/+ Index(<表名> <index 名>)/,通过这种方式强制使用索引。 例如:

select /*+ index(user,USER_NAME_AGE_INDEX) */ "name","sex" from user where "user_id"='123';

这样会强制查询索引表,但由于 sex字段其实不在索引表,最后还是会去查询原表,但可能会缩小查询范围。
比如以 time 为查询条件,在原表需要查询所有 id 的部分 time,而先查询索引可以先过滤出满足查询条件的 id,再去原表查询满足条件的 id 的部分 time,如果过滤出来的 id 很少,性能会有显著提升,如果过滤出来的 id 非常多,性能可能就没有明显提升,甚至可能会有下降,因为要查两张表。因此只有当用户明确知道符合检索条件的数据较少的时候才适合使用,否则就又会造成全表扫描,对性能影响较大。

explain 命令

可以通过 explain 命令查看数据库是如何执行查询计划的。

explain select * from User where age > 20
索引对查询性能的影响

索引不一定能显著提升查询性能,这取决于数据分布和查询条件

如果是以 time 为查询条件,在原表需要查询所有 id 的部分 time,而在索引表是直接查询 time,这样如果满足查询条件的 id 很少,性能会有显著提升,如果满足查询条件的 id 本来就非常多,性能可能就没有明显提升

如果是以 sale 为查询条件,在原表需要查询所有 id 的所有 time,即需要查询原表所有 row key,而在索引表是直接查询 sale,一般来讲性能会有显著提升,除非满足查询条件的 id + time 非常多,即满足条件的原表 row key 非常多,那性能可能就没有明显提升。

对写性能的影响

索引会导致写性能下降,因为建了多少索引就要写多少张表,同时消耗更多的磁盘空间。所以,索引不是越多越好。

注意几点
  • Phoenix 可以帮助我们自动维护索引表数据的更新,但前提是,数据的更新采取的是 Phoenix API 的写入方式,至于以 HBase bulkload 的方式导入的数据,更不会引起索引表数据的更新了,各位看官可以自行尝试。
  • 当我们以非 Phoenix API 的方式写入数据的时候,在数据表中更新数据之后,还得需要在与之对应的索引表中插入拼装后的索引数据。保证索引表数据的完整性很重要,否则,即使不出现查询超时,也可能存在明明表里有数据,可就是查不到数据的尴尬场景。

最后,Phoenix 的二级索引在某些场景中还是非常有用的,可一旦使用姿势不当,极大可能会造成全表扫描,严重时线上其他的查询服务也会深受其害。其次牢记,不走 Phoenix 的 API 而更新数据的方式,索引表可能不会随之更新,必要时需要手动维护索引表数据,索引表的数据要严格与主表的数据保持一致,否则,会出现遗漏数据的情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值