Apache HBase 셸

Apache HBase 셸

이 문서는 Apache HBase 셸 사용법을 설명해요. HBase 셸은 (J)Ruby의 IRB에 HBase 특정 명령을 추가한 것이에요. IRB에서 할 수 있는 것은 무엇이든 HBase 셸에서 할 수 있어요. 테이블 생성부터 데이터 읽기/쓰기, 스크립트 작성까지 다룰 수 있는 실용적인 내용이에요.

출처: 문서

본문

Apache HBase 셸은 (J)Ruby의 IRB에 몇 가지 HBase 특정 명령을 추가한 것이에요. IRB에서 할 수 있는 것은 무엇이든 HBase 셸에서 할 수 있어야 해요.

HBase 셸을 실행하려면 다음과 같이 하세요.

$ ./bin/hbase shell

help를 입력하고 <RETURN>을 누르면 셸 명령과 옵션 목록을 볼 수 있어요. help 출력의 끝 부분 단락을 최소한 훑어서 HBase 셸에 변수와 명령 인자가 어떻게 입력되는지 감을 잡으세요. 특히 테이블 이름, 행, 열 등이 어떻게 따옴표로 묶여야 하는지 주목하세요.

기본 셸 운영 예시는 shell exercises를 참고하세요.

Rajeshbabu Chintaguntla가 정리한 모든 셸 명령의 잘 정리된 목록이 여기 있어요.

Ruby로 스크립팅

Apache HBase 스크립팅 예시는 HBase bin 디렉터리에서 찾아보세요. *.rb로 끝나는 파일을 보세요. 이 파일 중 하나를 실행하려면 다음과 같이 하세요.

$ ./bin/hbase org.jruby.Main PATH_TO_SCRIPT

비대화형 모드에서 셸 실행

HBase 셸에 새로운 비대화형 모드가 추가됐어요 (HBASE-11658). 비대화형 모드는 HBase 셸 명령의 종료 상태(성공 또는 실패)를 포착해 그 상태를 명령 인터프리터에 전달해요. 일반 대화형 모드를 사용하면 HBase 셸은 항상 자신의 종료 상태만 반환하는데, 성공 시 거의 항상 0이에요.

비대화형 모드를 호출하려면 HBase 셸에 -n 또는 --non-interactive 옵션을 전달하세요.

OS 스크립트에서의 HBase 셸

HBase 셸을 Bash 셸(대부분의 Linux와 UNIX 배포판의 기본 명령 인터프리터) 같은 운영체제 스크립트 인터프리터 안에서 사용할 수 있어요. 다음 지침은 Bash 문법을 사용하지만 csh나 tcsh 같은 C 스타일 셸에서 동작하도록 조정할 수 있고, Microsoft Windows 스크립트 인터프리터에서도 동작하도록 수정할 수 있을 거예요. 기여는 환영이에요.

이런 식으로 HBase 셸 명령을 생성하는 것은 느리므로, HBase 연산을 운영체제 명령줄과 결합하는 것이 언제 적절한지 결정할 때 명심하세요.

HBase 셸에 명령 전달

echo 명령과 |(파이프) 연산자를 사용해 비대화형 모드( hbase.shell.noninteractive 참고)에서 HBase 셸에 명령을 전달할 수 있어요. 셸이 해석할 HBase 명령의 문자는 이스케이프해야 해요. 아래 예시에서 일부 디버그 수준 출력은 잘렸어요.

$ echo "describe 'test1'" | ./hbase shell -n

Version 0.98.3-hadoop2, rd5e65a9144e315bb0a964e7730871af32f5018d5, Sat May 31 19:56:09 PDT 2014

describe 'test1'

DESCRIPTION                                          ENABLED
 'test1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'NON true
 E', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0',
  VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIO
 NS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS =>
 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false'
 , BLOCKCACHE => 'true'}
1 row(s) in 3.2410 seconds

모든 출력을 억제하려면 */dev/null:*로 echo 하세요.

$ echo "describe 'test'" | ./hbase shell -n > /dev/null 2>&1

스크립트 명령의 결과 확인

스크립트는 대화형으로 실행되도록 설계되지 않았으므로 명령이 실패했는지 성공했는지 확인할 방법이 필요해요. HBase 셸은 성공 명령에 0 값을, 실패 명령에 어떤 0이 아닌 값을 반환하는 표준 규칙을 사용해요. Bash는 명령의 반환 값을 $?라는 특수 환경 변수에 저장해요. 이 변수는 셸이 명령을 실행할 때마다 덮어쓰이므로, 결과를 스크립트에서 정의한 다른 변수에 저장해야 해요.

반환 값을 저장하고 그것에 기반해 결정을 내리는 한 가지 방법을 보여주는 초보적인 스크립트예요.

#!/bin/bash

echo "describe 'test'" | ./hbase shell -n > /dev/null 2>&1
status=$?
echo "The status was " $status
if ($status == 0); then
  echo "The command succeeded"
else
  echo "The command may have failed."
fi
return $status

스크립트에서 성공 또는 실패 확인

종료 코드 0을 얻는 것은 스크립트로 만든 명령이 확실히 성공했음을 의미해요. 그러나 0이 아닌 종료 코드를 얻는 것이 반드시 명령이 실패했다는 뜻은 아니에요. 명령이 성공했을 수도 있지만 클라이언트가 연결을 잃었거나, 어떤 다른 사건이 그 성공을 가렸을 수도 있어요. RPC 명령은 무상태(stateless)이기 때문이에요. 연산 상태를 확신하는 유일한 방법은 확인하는 것이에요. 예를 들어 스크립트가 테이블을 만들었지만 0이 아닌 종료 값을 반환하면, 다시 만들려고 시도하기 전에 테이블이 실제로 생성됐는지 확인해야 해요.

명령 파일에서 HBase 셸 명령 읽기

HBase 셸 명령을 텍스트 파일에 한 줄에 하나씩 입력하고 그 파일을 HBase 셸에 전달할 수 있어요.

예시 명령 파일

create 'test', 'cf'
list 'test'
put 'test', 'row1', 'cf:a', 'value1'
put 'test', 'row2', 'cf:b', 'value2'
put 'test', 'row3', 'cf:c', 'value3'
put 'test', 'row4', 'cf:d', 'value4'
scan 'test'
get 'test', 'row1'
disable 'test'
enable 'test'

HBase 셸이 명령을 실행하도록 지시

hbase shell 명령의 유일한 인자로 명령 파일의 경로를 전달하세요. 각 명령이 실행되고 그 출력이 표시돼요. 스크립트에 exit 명령을 포함하지 않으면 HBase 셸 프롬프트로 돌아가게 돼요. 각 개별 명령의 성공/실패를 프로그래밍 방식으로 확인할 방법은 없어요. 또한 각 명령의 출력은 보이지만 명령 자체는 화면에 echo 되지 않아 명령과 출력을 연결하기 어려울 수 있어요.

$ ./hbase shell ./sample_commands.txt
0 row(s) in 3.4170 seconds

TABLE
test
1 row(s) in 0.0590 seconds

0 row(s) in 0.1540 seconds

0 row(s) in 0.0080 seconds

0 row(s) in 0.0060 seconds

0 row(s) in 0.0060 seconds

ROW                   COLUMN+CELL
 row1                 column=cf:a, timestamp=1407130286968, value=value1
 row2                 column=cf:b, timestamp=1407130286997, value=value2
 row3                 column=cf:c, timestamp=1407130287007, value=value3
 row4                 column=cf:d, timestamp=1407130287015, value=value4
4 row(s) in 0.0420 seconds

COLUMN                CELL
 cf:a                 timestamp=1407130286968, value=value1
1 row(s) in 0.0110 seconds

0 row(s) in 1.5630 seconds

0 row(s) in 0.4360 seconds

셸에 VM 옵션 전달

HBASE_SHELL_OPTS 환경 변수를 사용해 HBase 셸에 VM 옵션을 전달할 수 있어요. 이 변수를 환경(예: ~/.bashrc 편집)에서 설정하거나 HBase 셸을 실행하는 명령의 일부로 설정할 수 있어요. 다음 예시는 HBase 셸을 실행하는 VM의 수명 동안만 몇 가지 가비지 컬렉션 관련 변수를 설정해요. 명령은 한 줄로 실행해야 하지만 가독성을 위해 \ 문자로 나눴어요.

$ HBASE_SHELL_OPTS="-verbose:gc -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps \
  -XX:+PrintGCDetails -Xloggc:$HBASE_HOME/logs/gc-hbase.log" ./bin/hbase shell

HBase 셸 시작 시 구성 무시(override)

hbase-2.0.5/hbase-2.1.3/hbase-2.2.0/hbase-1.4.10/hbase-1.5.0부터 hbase-*.xml에 지정된 hbase 구성을 명령줄에서 -D 접두사가 붙은 키/값을 전달해 무시하거나 전달할 수 있어요.

$ ./bin/hbase shell -Dhbase.zookeeper.quorum=ZK0.remote.cluster.example.org,ZK1.remote.cluster.example.org,ZK2.remote.cluster.example.org -Draining=false
...
hbase(main):001:0> @shell.hbase.configuration.get("hbase.zookeeper.quorum")
=> "ZK0.remote.cluster.example.org,ZK1.remote.cluster.example.org,ZK2.remote.cluster.example.org"
hbase(main):002:0> @shell.hbase.configuration.get("raining")
=> "false"

셸 요령

테이블 변수

HBase 0.95는 테이블에 대한 jruby 스타일 객체 지향 참조를 제공하는 셸 명령을 추가해요. 이전에는 테이블에 대해 동작하는 모든 셸 명령이 항상 테이블 이름을 인자로 받는 절차적 스타일이었어요. HBase 0.95는 테이블을 jruby 변수에 할당하는 기능을 도입해요. 테이블 참조는 puts, scans, gets 같은 데이터 읽기/쓰기 연산과 disabling, dropping, describing 같은 관리 기능을 수행하는 데 사용할 수 있어요.

예를 들어, 이전에는 항상 테이블 이름을 지정했어요.

hbase(main):000:0> create 't', 'f'
0 row(s) in 1.0970 seconds
hbase(main):001:0> put 't', 'rold', 'f', 'v'
0 row(s) in 0.0080 seconds

hbase(main):002:0> scan 't'
ROW                                COLUMN+CELL
 rold                              column=f:, timestamp=1378473207660, value=v
1 row(s) in 0.0130 seconds

hbase(main):003:0> describe 't'
DESCRIPTION                                                                           ENABLED
 't', {NAME => 'f', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_ true
 SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2
 147483647', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false
 ', BLOCKCACHE => 'true'}
1 row(s) in 1.4430 seconds

hbase(main):004:0> disable 't'
0 row(s) in 14.8700 seconds

hbase(main):005:0> drop 't'
0 row(s) in 23.1670 seconds

hbase(main):006:0>

이제 테이블을 변수에 할당하고 그 결과를 jruby 셸 코드에서 사용할 수 있어요.

hbase(main):007 > t = create 't', 'f'
0 row(s) in 1.0970 seconds

=> Hbase::Table - t
hbase(main):008 > t.put 'r', 'f', 'v'
0 row(s) in 0.0640 seconds
hbase(main):009 > t.scan
ROW                           COLUMN+CELL
 r                            column=f:, timestamp=1331865816290, value=v
1 row(s) in 0.0110 seconds
hbase(main):010:0> t.describe
DESCRIPTION                                                                           ENABLED
 't', {NAME => 'f', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_ true
 SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2
 147483647', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY => 'false
 ', BLOCKCACHE => 'true'}
1 row(s) in 0.0210 seconds
hbase(main):038:0> t.disable
0 row(s) in 6.2350 seconds
hbase(main):039:0> t.drop
0 row(s) in 0.2340 seconds

테이블이 이미 생성됐다면 get_table 메서드를 사용해 Table을 변수에 할당할 수 있어요.

hbase(main):011 > create 't','f'
0 row(s) in 1.2500 seconds

=> Hbase::Table - t
hbase(main):012:0> tab = get_table 't'
0 row(s) in 0.0010 seconds

=> Hbase::Table - t
hbase(main):013:0> tab.put 'r1' ,'f', 'v'
0 row(s) in 0.0100 seconds
hbase(main):014:0> tab.scan
ROW                                COLUMN+CELL
 r1                                column=f:, timestamp=1378473876949, value=v
1 row(s) in 0.0240 seconds
hbase(main):015:0>

list 기능도 확장되어 테이블 이름 목록을 문자열로 반환해요. 그런 다음 jruby를 사용해 이 이름들에 기반해 테이블 연산을 스크립트할 수 있어요. list_snapshots 명령도 유사하게 동작해요.

hbase(main):016 > tables = list('t.*')
TABLE
t
1 row(s) in 0.1040 seconds

=> ["t"]
hbase(main):017:0> tables.map { |t| disable t ; drop  t}
0 row(s) in 2.2510 seconds

=> [nil]
hbase(main):018:0>

irbrc

홈 디렉터리에 자신만의 .irbrc 파일을 만드세요. 사용자 정의를 추가하세요. 유용한 것 하나는 셸 호출 간에 명령이 저장되도록 하는 명령 히스토리예요.

$ more .irbrc
require 'irb/ext/save-history'
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"

각 식의 평가 결과가 stderr로 출력되는 것을 피하고 싶다면(예: "list" 명령에서 반환된 테이블 배열), 다음과 같이 하세요.

$ echo "IRB.conf[:ECHO] = false" >>~/.irbrc

다른 가능한 구성에 대해 알아보려면 .irbrc의 ruby 문서를 참고하세요.

LOG 데이터를 타임스탬프로

hbase 로그의 날짜 '08/08/16 20:56:29'를 타임스탬프로 변환하려면:

hbase(main):021:0> import java.text.SimpleDateFormat
hbase(main):022:0> import java.text.ParsePosition
hbase(main):023:0> SimpleDateFormat.new("yy/MM/dd HH:mm:ss").parse("08/08/16 20:56:29", ParsePosition.new(0)).getTime() => 1218920189000

반대 방향으로 가려면:

hbase(main):021:0> import java.util.Date
hbase(main):022:0> Date.new(1218920189000).toString() => "Sat Aug 16 20:56:29 UTC 2008"

HBase 로그 형식과 완전히 같은 형식으로 출력하려면 SimpleDateFormat을 좀 만져야 할 거예요.

셸 구성 쿼리

hbase(main):001:0> @shell.hbase.configuration.get("hbase.rpc.timeout")
=> "60000"

셸에서 구성을 설정하려면:

hbase(main):005:0> @shell.hbase.configuration.setInt("hbase.rpc.timeout", 61010)
hbase(main):006:0> @shell.hbase.configuration.get("hbase.rpc.timeout")
=> "61010"

HBase 셸로 테이블 사전 분할(pre-splitting)

HBase 셸 create 명령으로 테이블을 만들 때 다양한 옵션으로 테이블을 사전 분할할 수 있어요.

가장 간단한 접근은 테이블을 만들 때 분할 지점의 배열을 지정하는 것이에요. 문자열 리터럴을 분할 지점으로 지정하면 문자열의 기본 바이트 표현에 기반해 분할 지점이 만들어진다는 점에 주의하세요. 그래서 분할 지점 '10'을 지정하면 실제로 바이트 분할 지점 '\x31\30'을 지정하는 것이에요.

분할 지점은 n+1개의 region을 정의하는데, 여기서 n은 분할 지점의 수예요. 가장 낮은 region은 가능한 가장 낮은 키부터 첫 분할 지점 키 직전(첫 분할 지점 키 미포함)까지의 모든 키를 포함해요. 다음 region은 첫 분할 지점부터 다음 분할 지점 키 직전까지의 키를 포함해요. 이것은 마지막 분할 지점까지 계속돼요. 마지막 region은 마지막 분할 지점부터 가능한 최대 키까지로 정의돼요.

hbase>create 't1','f',SPLITS => ['10','20','30']

위 예시에서 테이블 't1'은 column family 'f'로 생성되고 4개의 region으로 사전 분할돼요. 첫 region은 '\x00'부터 '\x30'까지의 모든 키를 포함한다는 점에 주의하세요('\x31'이 '1'의 ASCII 코드이기 때문).

다음 변형을 사용해 분할 지점을 파일로 전달할 수 있어요. 이 예시에서 분할은 로컬 파일 시스템의 로컬 경로에 해당하는 파일에서 읽혀져요. 파일의 각 줄은 분할 지점 키를 지정해요.

hbase>create 't14','f',SPLITS_FILE=>'splits.txt'

다른 옵션은 원하는 region 수와 분할 알고리즘에 기반해 자동으로 분할을 계산하는 것이에요. HBase는 균등 분할(uniform splits) 또는 16진수 키 기반 분할을 위한 알고리즘을 제공하지만, 키 범위를 세분화하기 위해 자신의 분할 알고리즘을 제공할 수도 있어요.

# create table with four regions based on random bytes keys
hbase>create 't2','f1', { NUMREGIONS => 4 , SPLITALGO => 'UniformSplit' }

# create table with five regions based on hex keys
hbase>create 't3','f1', { NUMREGIONS => 5, SPLITALGO => 'HexStringSplit' }

HBase 셸이 사실상 Ruby 환경이므로, 간단한 Ruby 스크립트를 사용해 분할을 알고리즘적으로 계산할 수 있어요.

# generate splits for long (Ruby fixnum) key range from start to end key
hbase(main):070:0> def gen_splits(start_key,end_key,num_regions)
hbase(main):071:1>   results=[]
hbase(main):072:1>   range=end_key-start_key
hbase(main):073:1>   incr=(range/num_regions).floor
hbase(main):074:1>   for i in 1 .. num_regions-1
hbase(main):075:2>     results.push([i*incr+start_key].pack("N"))
hbase(main):076:2>   end
hbase(main):077:1>   return results
hbase(main):078:1> end
hbase(main):079:0>
hbase(main):080:0> splits=gen_splits(1,2000000,10)
=> ["\000\003\r@", "\000\006\032\177", "\000\t'\276", "\000\f4\375", "\000\017B<", "\000\022O{", "\000\025\\\272", "\000\030i\371", "\000\ew8"]
hbase(main):081:0> create 'test_splits','f',SPLITS=>splits
0 row(s) in 0.2670 seconds

=> Hbase::Table - test_splits

HBase 셸 명령 truncate는 효과적으로 테이블을 기본 옵션으로 drop하고 재생성하기 때문에 사전 분할을 폐기한다는 점에 주의하세요. 사전 분할된 테이블을 truncate해야 한다면, 사용자 지정 분할 옵션을 다시 지정하기 위해 테이블을 명시적으로 drop하고 재생성해야 해요.

디버그

셸 디버그 스위치

명령을 실행할 때 더 많은 출력(예: 예외 시 더 많은 스택 트레이스)을 보기 위해 셸에서 디버그 스위치를 설정할 수 있어요.

hbase> debug <RETURN>
DEBUG 로그 레벨

셸에서 DEBUG 수준 로깅을 활성화하려면 -d 옵션으로 실행하세요.

$ ./bin/hbase shell -d

명령

count

count 명령은 테이블의 행 수를 반환해요. 올바른 CACHE로 구성하면 꽤 빠르죠.

hbase> count '<tablename>', CACHE => 1000

위 count는 한 번에 1000행을 가져와요. 행이 크면 CACHE를 낮추세요. 기본값은 한 번에 한 행을 가져오는 것이에요.

더 알아보기 (Learn more)