1.あなたはなぜセカンダリインデックスが必要なのでしょうか?
あなたは正確に行レコードを検索したい場合はHBaseのために、唯一の方法は、のrowKeyによってクエリにあります。rowKeyでデータを検索しない場合は、全表スキャンで、各列、行ごとの値を比較する必要があります。大きなテーブルの場合は、全表スキャンのコストは受け入れられません。しかし、多くの場合、複数の角度からのクエリのデータにする必要があります。例えば、人物を特定する際に、異なる視点名、ID番号、学生番号などを通じて照会することができ、のrowKeyにデータを入れるために、非常に多くの角度が(ほとんど不可能ですビジネスの俊敏性を認めていません、 rowKey長さの要件が許可されていません)。そのため、必要セカンダリインデックス(セカンダリインデックス)は、これが終わらします。セカンダリインデックスの原理は非常に単純ですが、彼らは維持するならば、それはいくつかの作業がかかります。さて、フェニックスは、HBaseのセカンダリインデックスのサポートを提供してきました。
2.フェニックスグローバルインデックスとローカルインデックス作成
2.1グローバル・インデックス
グローバルインデックス作成のためのグローバルインデックス少ない読み書きビジネスシナリオを。、データ書き込み時に大きなオーバーヘッドをインデックス使用グローバルデータテーブル(DELETE、UPSERT値およびUPSERT選択するすべての更新があるため )、 インデックステーブルを更新するためにつながる、インデックステーブルは、異なるデータ・ノードに分散され、ノード間のデータ送信は、オーバーヘッドより高いパフォーマンスをもたらしました。データを読み出す場合、フェニックスは、クエリによって消費される時間を短縮するために、インデックステーブルを選択します。あなたは、フィールドは、テーブル内のフィールドをインデックス化されていないクエリを実行する場合は、インデックスは、クエリの速度を改善しないであろうこと、デフォルトで使用されることはありません。
2.2ローカルのインデックス作成
ローカルインデックス、書き込み操作と頻繁スペースに制約のあるシナリオのローカル索引。インデックスを利用した場合、グローバルインデックスと同じように、フェニックスは自動的にクエリかどうかを判断します。あなたがローカル索引を使用すると、データテーブル内の同じサーバーを格納したデータとインデックスデータので、書き込みインデックスを避けるには、テーブルへの書き込み動作のオーバーヘッドの時に異なるインデックスサーバーをもたらします。クエリ速度を向上させるもたらすクエリはインデックステーブルのインデックスフィールドが使用されますない場合でも、ローカルインデックスフィールドを、使用している場合、これはグローバルインデックスと異なっています。ローカルのインデックス作成のために、データ・テーブルのすべてのインデックスデータは、別個の単一共有可能なテーブルに格納されています。
3.不変インデックスと変更可能なインデックス
3.1不変インデックス
不変インデックスは、インデックスは、イベントデータを格納されているようなログデータとして、データのみが状況(時系列データ)に応じて時系列順に更新され、格納されていないの増加のために変更等することができません。不変の収納指数は、唯一の追記書き込みです。フェニックスを使用してテーブルのステートメントを作成するときIMMUTABLE_ROWSを指定する場合= trueが、テーブルのインデックスに作成されたインデックスは不変のように設定されていることを示します。フェニックスのデフォルトテーブルを作成するときに指定されていないIMMUTABLE_ROW = trueの場合、テーブルが変更可能なように表現されます。インデックスと2種類のローカル不変インデックスに不変のグローバル不変インデックス。
3.2可変インデックス
可変インデックス、シーンチェンジに追加されたデータのための変数のインデックス。IMMUTABLE_ROWS = trueの時にテーブルを作成するには、明示的に指定しない限り、インデックスが作成したフェニックス・デフォルト・インデックスは、可変です。可変インデックスは、グローバル不変インデックスとローカル不変インデックス2種類に分けられます。
HBaseのサポートフェニックスセカンダリインデックスの設定
次の2つのインデックスフェニックス、各リージョンサーバーの追加HMASTERの構成やHBaseの-site.xmlのための必要性を有効にする場合。まず、各リージョンサーバーのHBaseの-site.xml内の次の属性を追加します。
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
<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>
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
</property>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
プロパティ上記の各リージョンサーバー構成の内側にはHBaseの-site.xmlの場合は、次のようにCREATE INDEXステートメントを使用して、セカンダリインデックスを作成すると、例外がスローされます。
Error: ERROR 1029 (42Y88): Mutable secondary indexes must have the hbase.regionserver.wal.codec property set to org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec in the hbase-sites.xml of every region server tableName=TEST_INDEXES (state=42Y88,code=1029)
- 1
その後、マスターの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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上記の変更はHBaseのクラスタ構成の再起動後に有効になります。
[特別の注意]
あなたはCDHを使用している場合はHBaseの、設定以上のClouderaのManager管理HBaseの増加の内部ページ内のHBase-site.xmlの項目で「設定」ページへの必要性を展開、および管理ページ内のコンフィギュレーション・テイク効果を作るためにHBaseのを再起動します。
5.テストケース
5.1テストケース1
HBaseの千ワットデータ
テーブルusers_test
【グローバルインデックス】
ユーザーの前にUSER_NAMEの特定のクエリは、セカンダリインデックス情報は16秒程度ほどかかり作成しませんでした。
コラムUSER_NAME上記の2次索引を作成します。
create index USERS_TEST_IDX0 on "users_test ("info".USER_NAME)
- 1
特定のUSER_NAMEミリ秒レベルのユーザー名のクエリ時間のかかる二次インデックスを作成した後
[注] セカンダリインデックスは説明コマンドによって使用されるかどうかを問い合わせることができます
【注意】 如果在select条件里面选择了其他的列,如USER_NO,因为该列没有存在于索引表,因此查询不会走索引表。
如果想在select USER_NAME,USER_NO查询仍然走索引,必须创建如下索引:
- 方式一,采取INCLUDE(index cover,即索引覆盖)的方式:
create index USERS_TEST_IDX1 on "users_test"("info".USER_NAME) INCLUDE("info".USER_NO)
- 1
索引覆盖其实就是将INCLUDE里面包含的列都存储到索引表里面,当检索的时候就可以从索引表里直接带回这些列值。要特别注意索引列和索引覆盖列的区别,索引列在索引表里面是以rowkey的形式存在,多个索引列以某个约定的字节分割然后一起存储在rowkey里面,也就是说当索引列有很多个的时候,rowkey的长度也相应会变长,大小取决于索引列值的大小。而索引覆盖列,是存储在索引表的列族中。
- 方式二,采取多列索引:
create index USERS_TEST_IDX2 on "users_test"("info".USER_NAME, "info".USER_NO)
- 1
【说明】
多列索引在满足前缀式的情况才会用到,如创建了A,B,C顺序的多列索引,当在where条件指定A条件、A B条件或者A B C条件均会走索引,但是 B C条件则无法走索引。
【Local Indexing】
在users_test表创建local index类型的二级索引:
create local index USERS_TEST_LOCAL_IDX ON "users_test"("info".USER_NAME)
- 1
与Global Indexing不同的是,如果select子句里面带有除了索引列(USER_NAME)以外的列,仍然可以走索引表。
【说明】
创建Local Indexing时候指定的索引名称会与实际创建在Hbase里面的表名称不一致,这应该是Phoenix做了映射的关系,而且对于同一个Hbase里面的table创建多个Local Indexing,索引表在Hbse list命令查询的时候也只有一个。
5.2 测试案例2
HBase 1e数据
ammeter_test表
【Global Indexing】
create index AMMETER_TEST_IDX
on AMMETER_TEST ("info"."ammeter_no1", "info"."ammeter_no2") include("info"."ammeter_price");
- 1
- 2
(1) 条件查询包含rowkey
> explain select * from AMMETER_TEST where "info"."ammeter_no1" = '11000000005281' AND "ammeter_no2" = '11000000001004' and ROW = '11000002310462'
> select * from AMMETER_TEST where "info"."ammeter_no1" = '11000000005281' AND "ammeter_no2" = '11000000001004' and ROW = '11000002310462'
- 1
- 2
(2) 条件查询不包含rowkey但满足二级索引查找条件
> explain select ROW,"ammeter_price" from AMMETER_TEST where "info"."ammeter_no1" = '11000000005281' and "ammeter_no2" = '11000000001004'
> select ROW,"ammeter_price" from AMMETER_TEST where "info"."ammeter_no1" = '11000000005281' and "ammeter_no2" = '11000000001004' LIMIT 5
- 1
- 2
【分析】
- 对于包含rowkey的条件查询,Phoenix会启用服务器端过滤器快速筛选匹配的行并返回,亿级数据也能达到毫秒级别响应。
- 对于没有包含rowkey的条件查询,如果条件满足Phoenix二级索引查找,Phoenix会查二级索引表并快速返回记录。
6. 同步创建索引与异步创建索引
前面所讲的创建索引为同步创建索引,当执行create index的时候,索引表会直接与源数据表进行同步。但是,有时候我们的源表数据量很大,同步创建索引会抛出异常。异常信息大致如下所示:
15/12/11 14:20:08 WARN client.ScannerCallable: Ignore, probably already closed
org.apache.hadoop.hbase.UnknownScannerException: org.apache.hadoop.hbase.UnknownScannerException: Name: 37, already closed?
at org.apache.hadoop.hbase.regionserver.RSRpcServices.scan(RSRpcServices.java:2092)
at org.apache.hadoop.hbase.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:31443)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2035)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:107)
at org.apache.hadoop.hbase.ipc.RpcExecutor.consumerLoop(RpcExecutor.java:130)
at org.apache.hadoop.hbase.ipc.RpcExecutor$1.run(RpcExecutor.java:107)
at java.lang.Thread.run(Thread.java:745)
at sun.reflect.GeneratedConstructorAccessor13.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.apache.hadoop.ipc.RemoteException.instantiateException(RemoteException.java:106)
at org.apache.hadoop.ipc.RemoteException.unwrapRemoteException(RemoteException.java:95)
at org.apache.hadoop.hbase.protobuf.ProtobufUtil.getRemoteException(ProtobufUtil.java:313)
at org.apache.hadoop.hbase.client.ScannerCallable.close(ScannerCallable.java:329)
at org.apache.hadoop.hbase.client.ScannerCallable.call(ScannerCallable.java:184)
at org.apache.hadoop.hbase.client.ScannerCallableWithReplicas.call(ScannerCallableWithReplicas.java:136)
at org.apache.hadoop.hbase.client.ScannerCallableWithReplicas.call(ScannerCallableWithReplicas.java:56)
at org.apache.hadoop.hbase.client.RpcRetryingCaller.callWithoutRetries(RpcRetryingCaller.java:200)
at org.apache.hadoop.hbase.client.ClientScanner.call(ClientScanner.java:288)
at org.apache.hadoop.hbase.client.ClientScanner.close(ClientScanner.java:507)
at org.apache.phoenix.iterate.ScanningResultIterator.close(ScanningResultIterator.java:49)
at org.apache.phoenix.iterate.TableResultIterator.close(TableResultIterator.java:95)
at org.apache.phoenix.jdbc.PhoenixResultSet.close(PhoenixResultSet.java:162)
at org.apache.phoenix.compile.UpsertCompiler.upsertSelect(UpsertCompiler.java:199)
at org.apache.phoenix.compile.UpsertCompiler.access$000(UpsertCompiler.java:114)
at org.apache.phoenix.compile.UpsertCompiler$UpsertingParallelIteratorFactory.mutate(UpsertCompiler.java:229)
at org.apache.phoenix.compile.MutatingParallelIteratorFactory.newIterator(MutatingParallelIteratorFactory.java:62)
at org.apache.phoenix.iterate.ParallelIterators$1.call(ParallelIterators.java:109)
at org.apache.phoenix.iterate.ParallelIterators$1.call(ParallelIterators.java:100)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at org.apache.phoenix.job.JobManager$InstrumentedJobFutureTask.run(JobManager.java:183)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.hadoop.hbase.ipc.RemoteWithExtrasException(org.apache.hadoop.hbase.UnknownScannerException): org.apache.hadoop.hbase.UnknownScannerException: Name: 37, already closed?
at org.apache.hadoop.hbase.regionserver.RSRpcServices.scan(RSRpcServices.java:2092)
at org.apache.hadoop.hbase.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:31443)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2035)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:107)
at org.apache.hadoop.hbase.ipc.RpcExecutor.consumerLoop(RpcExecutor.java:130)
at org.apache.hadoop.hbase.ipc.RpcExecutor$1.run(RpcExecutor.java:107)
at java.lang.Thread.run(Thread.java:745)
at org.apache.hadoop.hbase.ipc.RpcClientImpl.call(RpcClientImpl.java:1199)
at org.apache.hadoop.hbase.ipc.AbstractRpcClient.callBlockingMethod(AbstractRpcClient.java:216)
at org.apache.hadoop.hbase.ipc.AbstractRpcClient$BlockingRpcChannelImplementation.callBlockingMethod(AbstractRpcClient.java:300)
at org.apache.hadoop.hbase.protobuf.generated.ClientProtos$ClientService$BlockingStub.scan(ClientProtos.java:31889)
at org.apache.hadoop.hbase.client.ScannerCallable.close(ScannerCallable.java:327)
... 20 more
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
这个时候,我们可以采用异步创建索引,方式如下:
CREATE INDEX async_index ON my_schema.my_table (v) ASYNC
- 1
通过create index的时候指定 ASYNC
关键字来指定异步创建索引。执行这个命令之后并不会引起索引表与源表的直接同步。这个时候查询并不会使用这个索引表。那么索引数据的导入还需要采用phoenix提供的索引同步工具类 IndexTool
, 这是一个mapreduce工具类,使用方式如下:
${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
- 1
- 2
- 3
当mapreduce任务执行结束,这个索引表才会变成可用。
7. 参考
(1) 可变索引与不可变索引
(3) Local Indexing
(4) 配置二级索引及测试
(5) Phoenix官网文档
因怕原文丢失,转载自:https://blog.csdn.net/d6619309/article/details/50358592