二级索引
HBase 二级索引方案
HBase 中的行是以 RowKey 的字典序排序存储。所以对于 HBase 的快速查询主要有两种场景,一种是根据 RowKey 快速查询单行数据,另一种是基于 RowKey 的前缀查询。由于 HBase 只有 RowKey 的一级索引,根据其他的字段进行查询效率是很低的,实际业务场景中可能需要引入二级索引。
我所了解的 HBase 二级索引的解决思路有如下几种:
- 官方的解决方案是使用 Coprocessor(类似触发器),自定义写入逻辑,当有数据写入时同时写入一份索引表。
- 可以使用其他的工具在外部构建和维护索引关系,索引字段和 RowKey 的对应关系,比如使用 Elasticsearch、MongoDB 等。
- 使用 Phoenix 构建二级索引。
Coprocessor 的方案由于运行都交给了 Regionserver,侵入性比较强,会对 Regionserver 的性能造成影响。第一版的时候我使用了 MongoDB 维护了索引关系,后期数据量达到 20 亿,对 MongoDB 所在的服务器造成了很大的压力,所以后期决定使用基于 HBase 的 Phoenix 构建二级索引。
Phoenix 二级索引
官方文档:http://phoenix.apache.org/secondary_indexing.html
索引特性
Covered Indexes(覆盖索引)
覆盖索引指的是把查询相关的字段都指定在索引中,这样查询就只需要在索引表中查询就行。建立索引的语句如下,把查询的字段(where条件字段)放在 ON 语句中,把其他需要查询出来的字段(其他 select 字段)放在 INCLUDE 中。
Phoenix 的索引其实就是维护一张 HBase 表(本地索引与原始数据同一张表),把索引的字段作为 RowKey,把 INCLUDE 的字段放在 COLUMN 中。所以索引的顺序也注意,需要把条件中一定有的字段放在前面,可能有的字段放在后面。
1 | CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3); |
Functional Indexes(函数索引)
索引的字段不局限与列,支持任意的表达式来创建索引。
1 | CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME)); |
构建索引
Phoenix 支持两种类型的索引机制,Global Indexes(全局索引) 和 Local Indexes(本地索引),不同的索引机制适用于不同的业务场景,默认是构建全局索引。全局索引是维护了一张新的索引表,本地索引的索引数据存在了原始数据表中(4.8.0之后)。
Global Indexes(全局索引)
Global Indexes(全局索引) 适用于大量读取操作的场景。所有数据的更新和写操作都需要更新相关的索引表,所以开销主要发生在写入阶段,查询是直接查询索引表,所以查询开销较小。由于只会查询索引表,所以在建立索引时需要合理设计 INCLUDE 字段,把需要查询的字段都放进去。
Local Indexes(本地索引)
Local Indexes(本地索引) 适用于大量写入操作的场景。本地索引的索引数据放在了数据表中(从4.8.0开始,我们将所有本地索引数据存储在同一数据表中的单独阴影列族中),索引数据和原始数据是在同一台机器上面,所以没有额外的网络开销。本地索引可以使用在没有全面覆盖 INCLUDE 的场景。
索引实例
异步创建索引
1 | # 先创建异步索引 |
创建索引
1 | CREATE INDEX my_index ON my_table (v1) INCLUDE (v2); |
与CREATE TABLE语句一样,CREATE INDEX语句可以传递属性以应用于基础HBase表,包括对其进行加盐的能力,如果主表被加盐,则索引将以相同的方式自动为全局索引加盐。使用本地索引时,不允许指定SALT_BUCKETS。
创建本地索引
1 | CREATE LOCAL INDEX my_index ON my_table (v1) |
查询时指定索引
1 | SELECT /*+ INDEX(my_table my_index) */ v2 FROM my_table WHERE v1 = 'foo' |