@xtccc
2016-12-19T09:58:50.000000Z
字数 11625
阅读 3780
Phoenix
目录
创建Index的DDL语法为:
CREATE INDEX {index_name}
ON {data_table} ({columns_to_index})
INCLUDE ({columns_to_cover})
Global index适合大量读、小量写的场景:性能的开销发生在写数据的阶段。对于global index,数据表的更新操作(DELETE
, UPSERT VALUES
, UPSERT SELECT
)会被截获,接着与该数据表相关的索引表会被更新。
create index "idx" on "test" ("c");
在默认情况下,对于global index,只有当query中的全部column都被包含在index中,才会使用该索引。
对于global index,为了防止维护索引时发生死锁,我们可以让index update的优先级比data update的优先级更高,并让metadata rpc调用比data rpc调用的优先级更高。为了实现这一点,需要在每一个region server的hbase-site.xml中添加以下的配置:
<property>
<name>hbase.region.server.rpc.scheduler.factory.class</name>
<value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
<property>
<name>hbase.rpc.controllerfactory.class</name>
<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
Local index适合大量写、空间受约束的场景:index data与table data将会处于同一个server上,以防止由写数据发生的网络开销当query没有被完全覆盖时,local index也可以被使用。 与global index不同的是,一个表的所有local indexes都将共享地被存储在一个单独的表中。如果query用到了local index,那么数据所在的每一个region都必须被检查,因为并不能实现明确index data在哪一个region中。综上,对于local index,性能开销在读数据阶段。
Local immutable index由server-side维护。
create local index "idx" on "test" ("c");
注意:从0.98.6到0.98.8版本的HBase不支持创建local index。
与global index不同的是,即使一个query中的任何column都没有被包含在index内,local index也会使用索引,因为table与index data会处于同一个region server上,这可以保证lookup总是本地的。
为了data table与local index处于同一个region server上,需要对HBase master上的hbase-site.xml文件添加如下的配置:
<property>
<name>hbase.master.loadbalancer.class</name>
<value>org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.phoenix.hbase.index.master.IndexMasterObserver</value>
</property>
为了使得当data region merge发生时,local index region merge也能进行,需要向每一个region server的hbase-site.xml文件中添加以下的配置:
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
</property>
Index在创建时,也可以将其声明为salted index table,如下:
create index "idx" on "test" ("c") SALT_BUCKETS=10;
如果创建的是local index,则不能指定SALT_BUCKETS
。
如果数据表是salted table,则与其对应的index自动是salted。
对于Immutable table创建的index,是使用时速度更快。但是要求:immutable table中的数据只能追加,不能修改。这种table和index适合timeseries data。
方法:首先,通过IMMUTABLE_ROWS=true
来创建一个immutable table,然后为该table创建index。
> create table "test" ("pk" varchar not null primary key, "c" varchar) IMMUTABLE_ROWS=true;
> upsert into "test" values('pk-1', 'hi');
> select * from "test";
+------------------------------------------+------------------------------------------+
| pk | c |
+------------------------------------------+------------------------------------------+
| pk-1 | hi |
+------------------------------------------+------------------------------------------+
> create index idx on "test" ("c");
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-2', 'hello');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| hello | pk-2 |
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-2', 'abc');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| abc | pk-2 |
| hello | pk-2 |
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-1', 'abc');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| abc | pk-1 |
| abc | pk-2 |
| hello | pk-2 |
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
可以看到,对于immutable table,如果它的某条数据发生了修改,那么相应index并不会修改原来的索引记录,而是会再增加一条索引记录。当主表的数据更新后,该数据原来的索引记录依然存在,但是实际上是失效的。
对于immutable table,它里面的数据是可以修改的,但是它的index不会相应修改,而是会追加新的索引项。
对于global immutable index,index完全由client-side维护,
对于local immutable index,index完全由server-side维护。
在创建时不指定 IMMUTABLE_ROWS=true
,创建的就是mutable table,对应的index也是mutable index。
> create table "test" ("pk" varchar not null primary key, "c" varchar);
> upsert into "test" values('pk-1', 'hi');
> select * from "test";
+------------------------------------------+------------------------------------------+
| pk | c |
+------------------------------------------+------------------------------------------+
| pk-1 | hi |
+------------------------------------------+------------------------------------------+
> create index idx on "test" ("c");
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-2', 'hello');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| hello | pk-2 |
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-2', 'abc');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| abc | pk-2 |
| hi | pk-1 |
+------------------------------------------+------------------------------------------+
> upsert into "test" values('pk-1', 'abc');
> select * from idx ;
+------------------------------------------+------------------------------------------+
| 0:c | :pk |
+------------------------------------------+------------------------------------------+
| abc | pk-1 |
| abc | pk-2 |
+------------------------------------------+------------------------------------------+
可见,对于mutable table,如果某条数据被修改了,则index中对应的索引项也会相应修改。
对于某个表,如果它的索引表是immutable index,但是我们想
Index在HBase中实际上也是一个table,它把原始数据表的rowkey与column value揉和到了一起,成为了index table的rowkey。
Row keys are concatenated with index column values delimited by a zero byte character and end with data table primary key. If you define covered columns, you will see cells with their values as well in the index table.
例 : 首先创建数据表及其索引,并在数据表中写入一些数据
CREATE TABLE "test" (
"pk" VARCHAR NOT NULL PRIMARY KEY,
"c1" VARCHAR, "c2" VARCHAR);
CREATE INDEX "Idx_test_c1" ON "test" ("c1");
CREATE INDEX "Idx_test_c1c2" ON "test" ("c1") INCLUDE ("c2");
UPSERT INTO "test" ("pk-1", "c1-1", "c2-1");
UPSERT INTO "test" ("pk-2", "c1-2", "c2-2");
UPSERT INTO "test" ("pk-3", "c1-3", "c2-3");
UPSERT INTO "test" ("pk-4", "c1-3", "c2-4");
然后,看一看两个索引表中的实际数据在HBase是什么样的?
Apache Phoenix 4.5之后的版本才支持
首先创建一个table
create table TEST (ROW varchar not null, C1 varchar, C2 varchar, C3 varchar CONSTRAINT PK primary key (ROW));
然后向其中插入6,000,00条记录。
先看看在没有创建index的前提下,查询一条记录需要多长时间:
通过column value来查询一条记录,如果没有Index,需要4.063秒,漫长的时间。
我们创建了Index之后,再以同样的方式通过column value查询记录:
可以看到,在为表TEST的C1列创建了Index之后,我们在根据C1列来查询主键时,查询时间非常短(0.04秒),说明在查询时我们创建的Index起作用了。
注意:由于只为表TEST的C1列创建了Index,如果在查询时包含了除主键及C1列之外的其他列,那么Index就不会起作用(上图中的第一次查询已经说明了这个问题)。
也可以为多个columns建立索引,但是只能有1个列作为主索引列。
下面,为表TEST的C1列建立Index,同时为C2列建立Covered Index:
> create index "idx" on "TEST" ("C1") INCLUDE ("C2");
6,000,001 rows affected (53.661 seconds)
然后,尝试几种不同的QUERY:
可见,如果要保证Index有用,必须:
1. 被查询的列只能包含主键、C1和C2;
2. 必须含有C1列,因为C1列是主索引列;
在QUERY时,在索引起作用的情况下,是先通过主索引列C1查询出所有满足关于C1条件的记录,然后根据C2的值在这些数据中进行过滤。如果仅仅根据C2列的值进行查询,是无法使用Index的。
实际上,索引表idx
的rowkey中只包含C1列的内容,完全没有关于C2列的内容,C2列的数据被放在了索引表idx
的value中,这一点从HBase shell可以看出:
除了上例的INCLUDE
之外,还有下面的形式来对多列建立索引:
> create index "idx" on "TEST" ("C2", "C3");
6,000,001 rows affected (38.895 seconds)
这里,C2
是主索引列,如果QUERY中不包含C2
,Index依然无法起作用:
但是,索引表idx
中的内容就与INCLUDE
形式不同了:
当QUERY中包含了未被索引的column时,可以通过hint来让该QUERY使用Index:
> create index "idx" on "TEST" ("C1");
> select "ROW", "C2" from "TEST" where "C1"='c1-88';
+------------------------------------------+------------------------------------------+
| ROW | C2 |
+------------------------------------------+------------------------------------------+
| row-88 | c2-88 |
+------------------------------------------+------------------------------------------+
1 row selected (3.978 seconds)
> select /*+ INDEX("TEST" "idx") */ "ROW", "C2" from "TEST" where "C1"='c1-88';
+------------------------------------------+------------------------------------------+
| ROW | C2 |
+------------------------------------------+------------------------------------------+
| row-88 | c2-88 |
+------------------------------------------+------------------------------------------+
1 row selected (0.11 seconds)
可见,在select
与columns
之间加入/*+ INDEX(table_name index_name) */
,可以让QUERY强制使用index。这种用法要求:满足"C1"='c1-88'
如果我希望既可以单独按照C1列查询,也可以单独按照C2列查询,那怎么办?
方法是对同一个table,针对不同的column,分别创建各自的Index。例如,我们为TEST表的C1
列建立索引idx_C1
,在为C2
列建立索引idx_C2
。
但是,在查询时,QUERY中的column不能包含没有被索引的列,除非用hint来强制QUERY使用索引。
> create index "idx_C1" on "TEST" ("C1");
6,000,001 rows affected (42.06 seconds)
> create index "idx_C2" on "TEST" ("C2");
6,000,001 rows affected (36.526 seconds)
> select "C1" from "TEST" where "C1"='c1-77';
+------------------------------------------+
| C1 |
+------------------------------------------+
| c1-77 |
+------------------------------------------+
1 row selected (0.453 seconds)
> select "C2" from "TEST" where "C2"='c2-77';
+------------------------------------------+
| C2 |
+------------------------------------------+
| c2-77 |
+------------------------------------------+
1 row selected (0.045 seconds)
> select "C1", "C2" from "TEST" where "C1"='c1-77';
+------------------------------------------+------------------------------------------+
| C1 | C2 |
+------------------------------------------+------------------------------------------+
| c1-77 | c2-77 |
+------------------------------------------+------------------------------------------+
1 row selected (4.035 seconds)
> select "C1", "C2" from "TEST" where "C2"='c2-77';
+------------------------------------------+------------------------------------------+
| C1 | C2 |
+------------------------------------------+------------------------------------------+
| c1-77 | c2-77 |
+------------------------------------------+------------------------------------------+
1 row selected (5.015 seconds)
> select /*+ INDEX("TEST" "idx_C2") */ "C1", "C2" from "TEST" where "C2"='c2-77';
+------------------------------------------+------------------------------------------+
| C1 | C2 |
+------------------------------------------+------------------------------------------+
| c1-77 | c2-77 |
+------------------------------------------+------------------------------------------+
1 row selected (0.16 seconds)
drop index "idx" on "TEST";
如果数据表中被索引的列被删除了,那么对应的索引表也会被自动地删除。如果数据表中被覆盖的(covered)列被删除了,那么对应的索引表也会被自动删除。
create index "idx" on "TEST" (UPPER("v1"));
select "v1" from "TEST" where UPPER("v1")='HELLO';