Phoenix의 document 를 기반으로 작성한 보조 인덱스에 대한 내용이다.
뒷 부분으로 갈 수록 이해하기 어려워 뒷부분은 거의 document 내용 그대로이다.
- HBase 에서는 primary row key 에 의해 정렬된 단일 인덱스만 존재함
- primary row key 이외의 방법으로 레코드에 접근하는건 잠재적으로 table 을 full scan 하는 위험이 있음
- secondary indexing (인덱스로 형성한 column 이나 expression) 이 새로운 축을 따라 검색이나 range scan을 가능하게 함으로써 row key를 대체
covered indexes (index 페이지에서 row 식별자를 통해 index column 외의 데이터는 실제 데이터에 접근)
- 일종의 보조 인덱스
- index 에 등록되지 않은 컬럼을 정렬(cost 낭비 방지)없이 index 테이블에만 포함시키기 위한 방법
- index 테이블 탐색 후, 다시 데이터 테이블(주 테이블) 에 가서 나머지 컬럼들의 데이터를 가져오는 오버헤드를 줄일 수 있음
CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3)
functional indexes
- 함수를 index로 지정
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))
Global indexes
- 읽기(read) 작업이 무거운 경우에 쓰임
- 성능 패널티는 쓰기(write)에서 나타남
- 쓰기 작업에서의 데이터 테이블의 업데이터를 가로채서 인덱스 업데이트를 작업하여 관련된 인덱스 테이블들을 업데이트 함
- 읽기 작업 시, phoenix는 가장 빠른 쿼리 수행 시간을 산출하고 HBase table 처럼 직접 스캔하기 위해 위해 인덱스 테이블을 사용할지 말지 결정
- 기본적으로 hint 가 없는한 indexing 되지 않는 칼럼을 참조하는 쿼리에 대해서는 index 가 사용되지 않음
Local indexes
- 쓰기(write) 작업이 무거운 경우나 공간의 제약이 있는 곳에 쓰임
- global index 처럼, 쿼리 시 local index를 사용할 것인지 말 것인지 자동으로 phoenix가 결정
- local index 에서는 쓰기 작업 동안 네트워크 부하를 방지하기 위해 index 데이터와 table 데이터가 같은 서버에 존재함
- Local index 는 쿼리가 fully covered(즉, 조회하는 컬럼이 모두 index 에 포함되는 경우) 가 아니더라도 사용될 수 있음
(phoenix 는 자동으로 index 가 아닌 컬럼을 탐색함)
- global index 와는 달리, table 의 모든 local index 는 single, separate shared table 에 저장됨
- 읽기 시(read), local index 가 사용될 때는, 데이터를 찾기 위해 모든 region 를 반드시 탐색해야함 (index data 의 정확한 region 위치가 미리 결정될 수 없기 때문에)
Index Population
기본적으로 index 가 생성될 때, “CREATE INDEX” 호출 동안 동기적으로 이주생성됨 (data table ➔ index table 이란 의미겠지?)
- 이러한 방법은 data table 의 크기에 따라 실행 가능하지 못할 수도 있음
- index 생성문에 ASYNC 키워드를 이용하여 index 의 초기
이주를생성을 비동기식으로 수행할 수도 있음
CREATE INDEX async_index ON my_schema.my_table (v) ASYNC
- index table 을
이주생성시키는 map reduce 작업은 HBase 커맨드를 통해 반드시 분리되서 개시되어야 함
${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
--schema MY_SCHEMA --data-table MY_TABLE --index-table ASYNC_IDX
--output-path ASYNC_IDX_HFILES
- map reduce 작업이 완료되었을 때 index 는 활성화 되고 쿼리에서 사용될 수 있음
- map reduce 작업은 client 의 종료에 따라 유동적임
- output-path 옵션은 HFile 이 쓰여질 HDFS 디렉토리를 지정하는데 쓰임
Index Usage
피닉스는 쿼리를 서비스 하는데 인덱스를 사용하는 것이 좀 더 효율적인 방법으로 결정되면 index 를 자동으로 사용함
global index 는 쿼리에서 참조하는 모든 컬럼들이 index 에 포함되어 있지 않으면 index 가 사용되지 않음
아래 예는 쿼리가 index 를 사용하지 않음. v2 칼럼이 index 에 포함되어 있지 않음
SELECT v2 FROM my_table WHERE v1 = 'foo'
위 경우에 3 가지 index 가 사용되게 하기 위한 방법 3가지가 있음
1. covered index 생성
- v2 컬럼의 data 가 index table 로 copy 됨
- index 의 크키가 커짐
CREATE INDEX my_index ON my_table (v1) INCLUDE (v2)
2. index 가 사용되도록 hint 를 사용하여 강제
- index table 에 존재하지 않는 v2 컬럼의 값을 찾기 위해 data row 의 탐색 cost 발생
- hint 는 index 가 좋은 선택인지 알고 있는 경우에 사용하여만 함 (이 예제에서 ‘foo’의 데이터는 적음)
- 기본적으로 full scan 보다 성능이 나음
SELECT /*+ INDEX(my_table my_index) */ v2 FROM my_table WHERE v1 = 'foo'
3. local index 생성
- global index 와 달리, local index 는 쿼리에소 참조하는 컬럼들이 모두 index 에 포함되지 않더라도 index 가 사용됨
- data table 과 index table 이 같은 region 에 있기 때문에 local 탐색을 보장하므로 가능함
CREATE LOCAL INDEX my_index ON my_table (v1)
Index Removal
DROP INDEX my_index ON my_table
- Data table 에서 index 컬럼이 삭제되면, index 는 자동으로 삭제됨
- Data table 에서 covered 컬럼이 삭제되면, index 에서 자동으로 삭제됨
Index Properties
CREATE TABLE 처럼, CREATE INDEX 명령문도 HBase 테이블의 속성들을 적용할 수 있음(ex: salt)
CREATE INDEX my_index ON my_table (v2 DESC, v1) INCLUDE (v3)
SALT_BUCKETS=10, DATA_BLOCK_ENCODING='NONE'
SALT_BUCKETS=10, DATA_BLOCK_ENCODING='NONE'
- data table 이 salted 이면, index 는 자동적으로 같은 방법으로 salted 임 (global index의 경우)
- index 에 대한 MAX_FILESIZE 는 data, index tables 의 크기에 비례하여 아래로 조정된다.
- 반면 Local index 에는 salting 은 적용시킬 수 없음
Consistency Guarantees (일관성 보장)
- commit 후 클라이언트에게 return 이 성공이면, 모든 데이터는 관련된 index/data table에 쓰여지는 것이 보장됨
- 다시 말하면 HBase 에 의해 제공되는 강력한 일관선 보장(consistency guarantee)에 의해 index 갱신은 동기적임
- 그러나 index 는 data table 보다 분리되어 테이블에 저장되기 때문에, table 의 속성과 index 의 타입에 따라, 서버쪽의 충돌로 인한 commit 이 실패한 경우 table과 index 사이의 일관성은 서 다르다.(???)
- 설계 고려 시 매우 중요함
Transactional Tables
- table 을 transactional 로 선언함으로써, table 과 index 간, 최고 수준의 일관성을 얻을 수 있음
- 이 경우 table 변화에 대한 commit 과 관련된 index의 갱신은 강력한 ACID 보장과 함께 원자성(atomic)을 제공
- commit 실패 한다면, 아무 데이터도 갱신 되지 않으며, 이렇게 table 과 index 는 항상 동기화가 되어있음
- 그런데 왜 항상 테이블을 transactional 로 선언하면 안되는 것일까?
- table 이 immutable 로 선언되어 있다면, 이 경우 transactional 오버헤드는 매우 작음
- data 가 mutable 이면, transactional table 에서 발생하는 충돌 감지(conflict detection) 와 관련된 오버헤드, 그리고 transaction manager가 실행하면서 생기는 오버헤드를 수용가능 한지 확인해야함
- Secondary index 가 사용된 transactional table 들은 잠재적으로 data table에 쓰기 작업을 하는 가능성을 낮춤
- data table 과 secondary index 는 둘다 사용가능한 상태여야 함, 그렇지 않으면 쓰기 작업은 실패함
Immutable Tables
- table 의 데이터가 오직 한번만 쓰여지고 절대 업데이트가 되지 않는 다면, 증가하는 maintenance 에 대한 쓰기 오버헤드를 줄이기 위해 특정 최적화(optimization) 이루어질 수도 있음
- 로그나 이벤트 데이터 같은 time-series 데이터 들이 이런 경우
- IMMUTABLE_ROWS=true 속성을 사용함으로써 이런 최적화 장점을 얻을 수 있음
CREATE TABLE my_table (k VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true
- IMMUTABLE_ROWS=true 속성으로 선언된 모든 index 와 table 은 immutable 로 간주됨(dafult는 mutable)
- Global immutable index 에서는, data 변경으로 인해 발생하는 index table 의 index 들은 전적으로 client-side 에 유지됨 (???)
- Lobal immutable index 에서는, server-side 에 유지됨
- immutable table 로 선언된 테이블의 데이터가 실제로 갱신되지 않도록 하는 안전장치는 마련되어 있지 않음 (성능상의 이점이 무효화 됨)
- 이렇게 되면 더 이상 index 와 테이블은 동기화 되지 않음
- 기존의 immutable table 을 mutable 로 변경하고 싶다면 ALTER TABLE 을 사용
ALTER TABLE my_table SET IMMUTABLE_ROWS=false
- non-transactional 상의 index 에서 immutable table 은 commit 실패 시 자동적으로 처리할 수 있는 메커니즘이 없음
- table 과 index 간 일관성을 유지 하는 것은 핸들하기 위한 clietn 에 남겨짐(?????)
- 갱신 작업은 멱등이기 때문에
- 가장 간단한 해결책은 성공할 때까지 데이터 조작을 계속 재시도 하는 것
Mutable Tables (좀 이해가 잘 안되는 부분)
- non-transactional mutable table 에서, data table row의 WAL entry 에 index update 를 추가함으로써 index update 의 내구성을 유지
- WAL entry 가 디스크에 성공적으로 동기된 후에만, index/pirmary(data) table update를 시도
- index update 는 높은 처리율을 위해 기본적으로 parallel 하게 진행
- index update 를 하는 도중 server 가 충돌나면, WAL 복구 process 내의 index table에 모든 index update 를 재실해하고 정확성을 보장하기 위해 update의 멱등성에 의존함(?)
- non-transactional mutable table 의 index 는 오직 primary(data) table 뒤에서 항상 오직 single-batch
- 몇 가지 중요 포인트
- non-transactional table에서 data table하고 index 테이블하고 싱크가 안맞는 경우가 있음
- 매우 잠깐의 차이이고 매우 짧은 기간동안만 싱크가 안맞는 것이므로 괜찮음
- 각각의 데이터 row 와 index row들은 쓰여지거나 실패하거나가 보장됨 - hbase의 원자성 보장의 일부로 일부만 갱신된 적이 없음
- 데이터는 테이블에 먼저 쓰여지고 뒤이어 index 테이블에 쓰여짐 (WAL이 disabled 되어 있으면 반대)
1. Singular Write Path
- 실패 속성들을 보장하는 단일 쓰기 경로(singular write path) 라는 것이 존재
- HRegion 으로의 모든 쓰기는 phoenix 의 coprocessor 가 가로채고 나서 index update 를 구축
- 그리고 나서 index update는 본연의 update 를 위해 WAL entry 에 추가됨
- 만약 이 지점에서 어떤 실패라도 하면 클라이언트에 실패를 반환하고 데이터는 유지하지 않거나, 클라이언트에 표시함
- WAL 에 쓰여지고 나면, phoenix 는 index 와 data table 이 표시가능하는 것을 보장함 (실패한 경우라도)
- 만약 서버 장애가 발생하면, WAL 재시도 메커니즘으로 index update 를 재시도
- 만약 서버 장애가 아니면, 관련된 table에 index update 를 insert 함
- index update 가 실패하면, consistency 를 유지하는 다양한 방법들이 있음
- 오류가 나타난 경우 phoenix 의 system catalog table 에 접근할 수 없으면, phoenix 는 서버에 즉시 취소되고 실패되도록 강제하고 JMV의 System.exit 를 호출하여서버를 죽임
- 서버를 죽임으로써 WAL 이 복구를 재개하고, 적절한 table에 index update 를 재개하도록 보장함
- 이것은 secondary index의 사용을 지속할 수 없는 것을 보장함(유효하지 않은 상태)
2. Disallow table writes until mutable index is consistent (mutable index 가 일관될 때까지 table write 막기)
- non transactional table 과 index 간의 consistency 를 유지하는 최고 수준은 index를 update 하는 것이 실패한 경우에는 data table 에 write 하는 것을 임시적으로 막는 것임
- consistency mode 에서는 table과 index 는 오류가 나타나기 전의 timestamp를 가지고 있을 것이고, data table 에 데이터를 쓰는것은 index 가 online으로 돌아오고 data table 가 동기화가 될 때까지 막힐 것임
- index 는 active 로 유지되고 평상시처럼 쿼리에 의해 계속 사용될 것임
- Server-side 설정
- phoenix.index.failure.block.write
- commit 오류가 발생한 경우 index 가 data table 을 따라 잡을 때까지 data table 에 write 하는 것이 실패하도록 하기 위해서는 반드시 true 이어야 함
- phoenix.index.failure.handling.rebuild
- commit 오류가 발생한 경우 mutable index 가 백그라운드에서 rebuilding 이 가능하도록 하기 위해서는 반드시 true
- phoenix.index.failure.block.write
3. Disable mutable indexes on write failure until consistency restored (consistency 가 복구 될때까지 쓰기 실패시 mutable index를 비활성화)
- mutable index 들은 기본적으로 commit 시 쓰기가 실패하면 index 를 비활성화 하기 위해 index 에 마킹하고, 백그라운드에서 부분적으로 rebuild 하고 난 후, consistency 가 복구되고 나면 다시 active 상태로 index 를 마킹함
- consistency 모드에서, data table 에 write 하는 것은 secondary index 가 rebuild 되어 지는 동안에는 차단 되지 않음
- 그러나 secondary index 는 rebuild가 발생하는 동안 query 에 사용되지 않을 것임
- Server-side 설정
- phoenix.index.failure.handling.rebuild
- commit 에 실패한 경우 mutable index 가 백그라운드에서 rebuild 가능하게 하기 위해서는 반드시 true (default)
- phoenix.index.failure.handling.rebuild.interval
- mutable index 가 data table update 를 따라잡기 위해 부분적인 rebuild 가 필요한지 아닌지 확인하기 위해 서버 체크를 millisecond 주기로 제어
- 기본은 10000ms(10s)
- phoenix.index.failure.handling.rebuild.overlap.time
- 부분적인 rebuild 가 수행될 때, timestamp 에서 실패가 나타난 곳으로 얼마만큼 돌아가야 하는지 제어
- 기본은 1ms
- phoenix.index.failure.handling.rebuild
4. Disable mutable index on write failure with manual rebuild required
- mutable secondary index 에서 가장 낮은 수준의 consistency
- 이 경우, secondary index 쓰기가 실패했을 때, index 는 query 에 의해 다시 한번 사용되도록 요구되는 index 의 수동 rebuild 와 함께 비활성화 되도록 마킹됨
- Server-side 설정
- phoenix.index.failure.handling.rebuild
- commit 실패한 경우 mutable index 가 백그라운드에서 rebuild 되는 것으로부터 비활성화 하기 위해 false 로 설정해야 함
- phoenix.index.failure.handling.rebuild
Setup
- non-transactional 에서 mutable indexing 은 region server 와 master 에서 특별한 설정을 요구한다.
- Phoenix 는 table 에 mutable indexing 을 가능하기 위해 제대로 설정되도록 보장한다.
- 만약 정확하게 설정되지 않으면, secondary indexing 을 사용할 수 없음
- 모든 region server 의 hbase-site.xml 에 아래 설정을 추가한 후, 클러스터를 재시작
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
- 위 속성은 커스텀 WAL 가 가능하도록 하고 index update 의 적절한 writing/replay 를 보장함
- 이 코덱은 WALEdit 압축으로 알려진 WALEdit 옵션의 일반적인 host 를 지원함
<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>
<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>
- 위 속성은 data update 보다 index update 에 더 높은 우선순위를 제공함으로 써, global index 의 index 유지보수 동안의 데드락을 방지함
- 마찬가지로 data roc call 보다 metadata roc call 에 더 높은 우선순위를 제공함으로써 데드락을 방지
- Local indexing 은 data table 과 index 가 같은 region 에 있도록 master 에서 특별한 설정이 필요하다.(아래 설정을 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>
<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 병합에서 local index 병합을 지원하기 위해 모든 region server 의 hbase-site.xml 파일에 아래 내용 추가후 재시작
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
</property>
Tuning
- indexing 은 매우 빠르지만, 최적화 하기 위해 튜닝할 수 있는 속성들이 있다.
- 이 속성들은 hbase-site.xml 에 설정해야만 함
1. index.builder.threads.max
- data table update 시, index update 를 위해 사용되는 스레드 갯수
- 값을 증가시킬 수록 HRegion 의 row 상태를 읽는 bottleneck을 극복할 수 있음
- 이 값을 너무 높이면 일반적인 스레드-스와핑 문제 뿐만 아니라 너무 많은 동시 scan 요청을 제어할 수 없기 때문에 HRegion 에서 bottleneck 발생 할 수 있음
- Default: 10
2. index.builder.threads.keepalivetime
- 빌더 스레드 풀에서 스레드를 만료시키는 시간
- 사용되지 않는 스레드는 이 시간이 지난 후 바로 릴리즈 되고, core 스레드는 유지되지 않음 (매우 작은 우려이긴 하지만, table은 공평하게 변함없는 쓰기 부하가 유지되길 기대함)
- 그러나 동시에 예상되는 부하를 알지 못하면 우리에게 스레드 드랍을 허락함
- Default: 60
3. index.writer.threads.max
- index table 에 쓸 때 사용할 스레드 수
- 테이블 기준 당 병렬화 수준 - index table 의 수와 거의 일치함
- Default: 10
4. index.writer.threads.keepalivetime
- 쓰기 스레드 풀에서 만료되는 시간(초)
- 사용되지 않는 스레드는 이 시간이 지난 후 바로 릴리즈 되고, core 스레드는 유지되지 않음 (매우 작은 우려이긴 하지만, table은 공평하게 변함없는 쓰기 부하가 유지되길 기대함)
- 그러나 동시에 예상되는 부하를 알지 못하면 우리에게 스레드 드랍을 허락함
- Default: 60
5. hbase.htable.threads.max
- 쑤기를 위해 사용할 수 있는 index HTable 의 스레드 수
- 동시 index update 를 증가 시킴 (높은 처리량을 위해)
- Default: 2,147,483,647
6. hbase.htable.threads.keepalivetime
- HTbale 의 스레드 풀에서 만료되는 시간
- “direct handoff’ 방식을 사용하여, 필요하게 되면 새로운 스레드가 생성되고, 무한히 늘어남
- 이러한 접근법은 나쁠 수 있지만 HTable은 region server 수 만큼의 Runnable 을 생성함
- 그러므로 새로운 region 서바가 추가될 때 확장됨
- Default: 60
7. index.tablefactory.cache.size
- 캐시에 보관하는 index HTable 의 수
- 이 수를 늘리는 것은 index table 에 쓰기를 시도하는 각각의 시도에 HTable 재생성할 필요가 없게 되는것을 보장함
- 반대로, 너무 많이 높이면 메모리 압박이 있음
- Default: 10
8. org.apache.phoenix.regionserver.index.priority.min
- index 우선 순위 범위의 최소값을 지정
- Default: 1000
9. org.apache.phoenix.regionserver.index.priority.max
- index 우선 순위 범위의 최대값을 지정
- index min/max 내에서의 높은 우선수위는 update 를 먼저 처리하는 것을 의미하지는 않는다
- Default: 1050
10. org.apache.phoenix.regionserver.index.handler.count
- Global index 유지하기 위한 index 쓰기 요청을 처리할 때 사용하는 스레드 수
- 실제 스레드 수는 Max(call 큐의 수, handler count) 의 의해 좌우되더라도, call queue 의 수는 standard HBase 설정에 의해 결정됨c
- 더 나아가 큐를 튠하면, standard rpc queue 길이 파라미터를 조정할 수 있음 (현재 index queue 를 위한 특별한 방법은 없음)
- ipc.server.max.callqueue.length
- ipc.server.callqueue.handler.factor
- Default: 30
참조 : https://phoenix.apache.org/secondary_indexing.html
'Big Data > HBase&Phoenix' 카테고리의 다른 글
HDP 2.4 에서 phoenix 4.4 에서 4.8 버전으로 업그레이드 하기 (0) | 2016.08.22 |
---|---|
Phoenix - Joins (조인) (0) | 2016.08.17 |
Phoenix - Row timestamp (0) | 2016.08.15 |
Phoenix - Paged Queries (페이징 쿼리) (0) | 2016.08.14 |
Phoenix - Salted Tables (0) | 2016.08.12 |