Co2y's Blog

phoenix二级索引

背景

考虑一个多维度查询的例子,因为hbase只能根据rowkey查询,因此多维度查询只能利用filter来过滤,但是这是一个全表扫描,效率低下。也可以把各个维度拼到rowkey中,利用rowkey的filter,因为rowkey是字典序的,所以越靠前的维度效率越高,后面的就会退化成全表扫描。

这时候就需要用到二级索引来查询数据。

记录

本笔记不介绍有关二级索引的知识。 phoenix提供了多种二级索引,有global index, local index等。这里只记录这两种。phoenix利用coprocessor来动态维护索引表, 但这需要用phoenix提供的接口来写数据(upsert), 不能直接通过hbase的native API来插入数据。

  • 全局索引,官方介绍适合读多写少的场景,因为在upsert数据的时候,会触发coprocessor的钩子来更新索引表,降低写入效率。 全局索引可以有多个,每个全局索引都是一张单独的表,在查找数据的时候就是先查索引表得到rowkey,再查数据表。 如果所查的字段不在索引中, 默认是不会去查索引表, 这可以通过phoenix的hint, 让其强制触发。一个全局索引会存在一个region server上,会有多个region, 但是由一个regionserver来管理。

  • 本地索引,适合写多读少。所有的本地索引维护在一张共享的表中, 这样在查的时候因为不知道索引在哪个region中, 所以每个region都需要定位, 开销较大。

例子

目前做的一个例子是对大量数据做报表统计。 首先这部分数据是经过校验上报的, 因此本身不需要再修改, 也就是write once, 这样适合用全局索引。

在建索引的时候, 每个索引可以包括多个字段, 但是因为索引表也是一张普通的表, 所以第一个字段就相当于rowkey, 在查询的时候, 通常where子句后面的筛选条件如果只包含第一个字段, 那么是最快的,相当于两次rowkey定位。 如果有多个筛选条件,那么在rowkey的基础上, 再在索引表进行filter。 如果都不包括第一个字段, 那么就相当于对索引表全表扫描, 这样效率就不高了。

针对这个问题可以建立多个索引,全局索引的每个索引都是一张独立的表,可以每一个列都把它放置在rowkey的位置上,这样虽然每个字段的查询都变快了,但是写入会很慢。

总的原则就是,如果不能用索引缩减范围,那很多时候不如全表扫描。

优化

  • 无论是phoenix 还是 hbase 还是其他关系型数据库, 他们的连接数总是有限制的, 为了防止phoenix耗尽资源,需要限制线程数。

  • phoenix支持的bulk load很有限,只能是MR或者文件csv批量导入。

  • 此外因为phoenix提供了jdbc, 这可以通过jdbc的batchexecute来减少通信开销。

  • phoenix 索引表和数据表可能不同步, 但是考虑到报表对实时性要求没那么高, 可以接受。 phoenix集成了事务管理器tephra, 利用事务的ACID可以保证数据表和索引表的一致。