[발 번역] HBase Write Path

해당 블로그는 KT UCloud 의 지원을 받고 있습니다.

해당 글은 Cloudera의 http://www.cloudera.com/blog/2012/06/hbase-write-path/ 을 발 번역한 것입니다. 개인적으로 HBase 의 구조가 좀 궁금하기도 해서 번역을 해봅니다. 오역에 주의하세요.(개발자영어 분들의 도움을 얻어서 내용을 순화했습니다.)

Apache HBase 는 Hadoop database 입니다. 그리고 Hadoop 분산 파일 시스템(HDFS) 를 기반으로 동작합니다. HBase는 HDFS의 데이터에 랜덤 액세스를 하거나 데이터를 업데이트 할 수 있게 만들었습니다. 그러나 HDFS는 오직 파일에 내용을 추가하는 것만 가능하고,  파일이 생성된 후에는 수정이 불가능합니다.  그럼 분명히 궁금증이 생길껍니다. HBase는 어떻게 빠른 속도로 읽고 쓰는 작업을 수행하는가? 라고 말이죠. 해당 글에서 이것에 대해서 설명을 하고, HBase의 쓰기 과정이 어떻게 되는지, 즉, HBase 에서 어떻게 데이터를 업데이트 하는지 설명하려고 합니다.

쓰기 과정은 HBase 가 어떻게 put, delete 같은 명령을 처리하는지 입니다. 이 과정은 client 로 부터 시작해서 region server 로 넘어오고, 마지막으로 데이터가 HFile이라는 이름의 HBase 데이터 파일에 기록되면 이 쓰기 과정(write path)은 끝이 납니다. HBase의 쓰기 과정은 region server 장애시에 데이터 분실을 방지하기 위한 부분이 디자인시에 고려되었습니다. 따라서 쓰기 과정(write path) 에 대해 이해하면 HBase 자체의 데이터 유실 방지 방법에 대해 깊이 이해할 수 있습니다.

HBase table은 각각 서버 군에서 저장하고 관리하는데, 이 서버 군은 세 가지로 나눌 수 있습니다.

  1. active 마스터 서버 한 대
  2. 백업 마스터 서버 한대 이상
  3. Region 서버 여러 대

Region servers 는 HBase table을 다룹니다. HBase table이 매우 클 수 있기 때문에, region 이라고 불리는 여러 개의 파티션으로 나눠집니다. 각각의 Region 서버는 한 개 이상의 이런 region을 다룹니다.  region 서버 만이 HBase table data를 처리하는 유일한 서버라는 것을 기억해야 합니다. 그래서 마스터 서버의 장애는 데이터 유실을 야기하지 않습니다.(역자 주: HBase의 마스터 서버는 region을 어느 서버가 관리하고 있는지에 대한 정보만 저장하고 있습니다. 그래서 HBase의 마스터 서버가 장애가 나더라도 데이터가 유실되지 않습니다. 착각하기 쉬운 것이, Hadoop의 경우는 마스터 서버가 장애나고, Seconday 로 백업을 하더라도, 최신 데이터가 아니라면 데이터가 유실될 수 있습니다.)

HBase의 데이터는 구조적으로 Sorted map 과 유사합니다. 정렬된 키 공간으로 다른 샤드나 Region에 데이터를 저장합니다. HBase Client는 put 이나 delete 명령을 보냄으로써, table에 업데이트를 합니다. client 가 변경을 요청했을 때, 해당 요청은 기본적으로 바로 region 서버로 전달됩니다. 그러나,  계획에 따라, autoflush를 꺼둠으로써, 일단 client 측에서 변경을 캐시할 수 있습니다. 그리고 batch 형태로 region 서버에 해당 변경을 적용할 수 있습니다. autoflush 를 꺼두면, flush 명령이 실행될 때 까지 변경을 캐시하게 됩니다. 그리고 해당 버퍼의 크기는  “hbase.client.write.buffer” 파라매터로 설정된 크기나 프로그램적으로 버퍼의 크기를 설정할 수 있습니다.

Key가 정렬이 되면, 어떤 Region 서버가 해당 Key를 관리하는지 쉽게 알 수 있습니다. 특정 row에 변경 요청이 생기면, 각 row key 를 포함하는 특정 region 은 하나의 Region 서버에 의해서 서비스 됩니다. put/delete 되는 key에 따라, HBase client는 적합한 region 서버의 위치를 알 수 있습니다. 첫번째로, ZooKeeper quorum 으로 부터 ROOT-region 의 위치를 알게 됩니다. ROOT-region 으로 부터 client는 META-region의 위치를 발견하게 되고,  마지막으로 META-region 서버로 부터, 실제 서비스를 처리할 region 서버의 위치를 알게 됩니다. 이 세 단계가 진행하기 전에, region 서버의 위치가 캐시되어 있으면, 이런 비싼 작업을 피할 수 있습니다. 만약 캐시된 정보가 올바르지 않다면( 예를 들어, unknown region 예외가 발생한다면) 캐시를 업데이트 하고 region을 재배치하게 됩니다.

region 서버에서 요청을 받게 된 후에는, 해당 변경 사항이 바로 HFile에 쓰여지지는 않습니다. HFile의 데이터는 row key에 의해서 정렬되어야 하기 때문입니다. 이로 인해, 읽기 시에 랜덤 row를 효과적으로 찾을 수 있습니다. 데이터는 랜덤하게 HFile에 저장되지 않고 대신, 새로운 파일에 변경이 저장되어야 할껍니다. 그런데, 만약 매 업데이트가 파일에 저장되면, 많은 적은 크기의 파일들이 생성될 것입니다. 나중에 이걸 읽어서 합치는 것은 확장성이 있지도 않고, 효과적이지 못합니다. 그래서, 변경 사항은 바로 새로운 HFile 에 저장되지 않습니다.

각 변경 사항은 memstore 라고 불리는 싸고 랜덤 액세스에 효과적인 메모리에 저장되게 됩니다. memstore 안의 데이터는 HFile의 데이터 처럼 정렬됩니다. memstore 가 충분한 데이터를 가지게 되면, 전체 정렬된 데이터는 HDFS 내의 새로운 HFile에 저장되게 됩니다.( 역자 주: 그냥 충분한 데이터가 차면 고대로 그 부분을 저장한다는 겁니다. ) 하나의 큰 쓰기 작업을 처리하는 것이 효과적이고 HDFS의 장점을 취하는 방법입니다.

Write Path

memstore 에 데이터를 쓰는 것이 효과적이긴 하지만, 몇가지 위험도 가지고 있습니다.  memstore에 저장된 데이터는 휘발성 메모리에 저장됩니다. 그래서 system이 장애가 나면, 모든 memstore의 데이터는 유실이됩니다.  이런 위험을 피하기 위해서 HBase 는 memstore에 데이터를 쓰기 전에 Write-ahead-log(WAL) 에 업데이트를 저장합니다. (역자 주: 보통 transaction 등을 지원하는 일반적인 RDBMS에서 이 WAL이라는 기법을 아주 예전부터 사용하고 있습니다. 당연히 WAL을 사용하면 성능은 디스크 접근으로 인해서 떨어질 수 밖에 없습니다. hadoop append 가 되기 전에는 여기서도 유실의 가능성이 있었습니다. 자세한 건 다음에 ^^) 이 방법으로, region 서버가 장애가 나더라도 WAL을 이용해서 서버에 저장된 memstore 정보를 복구할 수 있습니다.

주의: 기본적으로 WAL이 켜져 있습니다. 그러나 WAL의 쓰기 과정에서 파일을 디스크에 저장하므로 리소스를 어느정도 사용하게 됩니다. WAL을 끈다면, 데이터를 유실할 수 있는 위험이 생깁니다. 만약 WAL을 끄기로 결정했다면, 자신만의 재해 복구 솔루션에 대해서 고민하고, 데이터 유실의 가능성에 대비해야 합니다.

WAL 안의 데이터는 HFile 과는 다르게 저장되어 있습니다. WAL은 변경 정보 리스트를 저장하고 있고, 하나의 변경 정보는 하나의 put/delete 를 나타냅니다. 해당 변경 정보는 변경 내역과, 어떤 Region에 변경이 있었는지를 저장합니다. 변경 정보는 시간순으로 기록되어 있고, 보존을 위해서 WAL 파일의 끝에 계속 추가되어서 디스크에 저장됩니다. WAL 파일의 경우 시간순으로 저장되므로, 랜덤으로 쓸 필요가 없습니다.

WAL 파일이 커지면, 해당 파일을 닫고, 새로운 WAL 파일을 생성해서 새로운 변경 정보르 저장하게 됩니다. 이것을 WAL 파일의 “Rolling” 이라고 합니다. WAL 파일이 바뀌면, 이전 파일에는 변경이 생기지 않습니다.(역자 주:  예전 서버들의 경우 파일시스템의 한계등으로 인해서 2GB 가 넘으면 로그 파일을 Rolling 하는 기능이 들어있는 서버들이 많았습니다. 그러나 HBase에서는 그것보다 훨씬 큰 파일들이 저장되게 됩니다.)

기본적으로, WAL 파일은 HDFS의 block size의 95% 정도 크기가 되면 바뀌게 됩니다.  “hbase.regionserver.logroll.multiplier” 파라매터를 이용해서 값을 설정할 수 있고 block size는 “hbase.regionserver.hlog.blocksize” 로 설정이 가능합니다. WAL 파일을 일정 간격으로 변경되도록 할 때는 “hbase.regionserver.logroll.period” 을 사용하면 됩니다. 기본적으로 한 시간으로 설정되어 이씁니다. WAL 파일의 크기가 설정 값 보다 작아도 한시간 마다 바뀌게 됩니다.

WAL 파일의 사이즈를 제한하는 것은, 복구가 필요할 때, 파일을 좀 더 효과적으로 이용할 수 있게 합니다.  이것은 특히 region의 WAL 파일을 이용할 때 특히 중요한데, 복구를 위해 파일을 이용하는 동안에, 해당 region을 이용할 수 없기 때문입니다.  결국 WAL 파일에 모든 변경을 기록하고, HFile의 내용을 영구적으로 저장하기 위해서 입니다. 이것이 끝나면, WAL 파일은 보관되고, LogCleaner 데몬 스레드에 위해서 결과적으로 삭제됩니다. WAL 파일은 데이터 복호를 위한 수단이라는 것을 기억해야합니다. WAL 파일은 오직 region 서버 장애 후에 데이터 복구시에만 이용하게 됩니다.  없으면 데이터가 유실되겠죠.

하나의 region 서버는 많은 region들을 서비스합니다. 그러나 각 region을 위한 WAL 파일은 가지고 있지 않습니다. 대신에,  하나의 WAL 파일이 region 서버에 의해서 모든 region이 공유하게 됩니다. WAL 파일이 정기적으로 변경되기 때문에 하나의 region 서버는 많은 WAL 파일을 가지게 됩니다. 한 순간에 하나의 region 서버에는 오직 하나의 WAL 파일에만 저장이 됩니다.

HBase의 root 가 “/hbase” 라고 가정하고, region server 인스턴스를 위한 모든 WAL 파일은 같은 root 폴더 밑에 저장됩니다. 다음과 같은 형태를 따르게 됩니다.

/hbase/.logs/<host>,
<port>,<startcode>

예를 들면:

/hbase/.logs/srv.example.com,60020,1254173957298

WAL 로그 파일은 다음과 같이 이름이 만들어집니다.:

/hbase/.logs/<host>,
<port>,<startcode>/<host>%2C
<port>%2C<startcode>.<timestamp>

예를 들면:

/hbase/.logs/srv.example.com,60020,1254173957298/srv.example.com%2C60020%2C1254173957298.1254173957495

WAL 파일안의 각각의 변경 정보는 유일한 sequence id를 가지고 있습니다. 해당 id는 변경 순서를 지켜서 증가합니다. 로그 파일이 변경될 때, 다음 sequence id 과 이전 파일 이름이 memory map 형태로 들어가서, memstore 가 디스크로 저장되고, 파일이 보관될 때, 각 WAL 파일의 최대 sequence id 쉽게 찾을 수 있습니다.

변경 정보와 sequence id들은 한 region 안에서 유일하고, 언제라도 WAL log에 추가될 수 있습니다. 변경 정보의 sequence id 는 항상 마지막 sequence id로 기록됩니다. memstore 정보가 디스크에 저장될 때, 해당 region을 위한 마지막 sequence id는 지워집니다. 디스크에 쓰여진 마지막 sequence id는 WAL 파일의 최대 sequence id와 동일합니다. region을 위한 하나의 WAL 파일안의 모든 변경 기록은 디스크에 저장이 된다고 할 수 있습니다.

WAL file은 변경과 memstore 의 flush는 서로 분리된 두 개의 액션입니다. 그리고 함께 실행되지 않습니다. 그러나, region 서버 장애의 경우에, 시간을 너무 많이 소모한ㄴ 것을 피하기 위해서, region 서버마다 너무 많은 WAL file을 유지하지를 원치 않습니다. 그러므로, WAL 파일이 변경될 때, HBase는 너무 많은 WAL 파일이 있는지 체크하고, 어떤 region을 디스크에 저장할 것인지 결정하고, 몇몇 WAL 파일을 보관하게 됩니다.

이번 포스트에서, HBase 의 쓰기 경로에 대해서 어떻게 HBase가 데이터를 생성하고 업데이트 하는지 설명하였습니다.  중요한 부분은 다음과 같습니다.

  1. Client 가 어떻게 region server를 알아내는가?
  2. Memstore 가 어떻게 빠른 랜덤 쓰기를 지원하는가?
  3. Region 서버 장애시에 데이터 유실을 피하는 방법으로의 WAL 파일

We will talk about HFile formats, WAL file splitting and so on in subsequent posts.