Hbase 메일링 리스트를 보면 대부분의 User들이 Region Server의 compation과 Hotspotting때문에 고생을 한다. 언젠가 부딪힐 수 있는 부분이라서 정리를 해본다.


Hbase 가이드에 따르면 샤딩해놓은 Region 서버를 미리 만들어 널널하게 데이터들을 분배할 수 있도록 하라고 한다. (pre creation) 마치DB 파티셔닝 정책과 비슷하다. 


보통은 Sequential key단위로 row를 Hbase에 저장하면,  하나의 Region Server에 몰릴 수 있다. 따라서 Key를 Sequential하지 않게 만드는 경우도 빈번하다.

이 방법을 해결하기 위해서는 2가지 정도가 쓰였던 것 같다. 


첫번째,  000001:key, 0000002:key 와 같이 key를 같이 쓰는 형태로 쓰게 된다. 대신 적절하게 key-value를 분산할 수 있지만 Range  Scan시 fully search하게 되니. Hbase에 큰 부하를 주게 한다.


두번째, consistent hashing key를 Hbase의 row로 사용하는 방식인데. 이 방법은 진짜 hashing은 되지만 Range는 도저히 쓸 수 없는 형태가 된다. 그렇지만 Region Hotspotting 을 피할 수 있게 된다. 


Range 검색도 쉽게 되게 하고 Hotspotting을 피하는 방법이 있나 궁금했었는데..

sematext.com 라는 빅데이터 분석 및 솔루션 판매 회사에서 기술 블로그에서 간단히 공유된 팁이 있어서 공유하고자 한다. 


http://blog.sematext.com/2012/04/09/hbasewd-avoid-regionserver-hotspotting-despite-writing-records-with-sequential-keys/


자세한 내용은 직접 보는 것이 좋을듯 하다.




Sequential Key를 row에 넣으면 특정 Region이 튀는 현상이 발견된다. 




아래 식을 이용해서 sequential key를 bucket number, 즉 저장 단위별로 %로 나누어 저장하게 한다. 

new_row_key = (++index % BUCKETS_NUMBER) + original_key





이렇게 함으로서, 데이터를 분산하여 cpu 튀는 현상이 없도록 했다. 





Scan시 병렬로 읽기 때문에 큰 성능 저하는 없었다고 한다.


이를 바탕으로 hbaseWD (https://github.com/sematext/HBaseWD) 라는 오픈 소스를 공유했다.



예를 들어, 32 buckets으로 나누고. 분산화된 키를 가지고 저장(Put)한다.


 byte bucketsCount = (byte) 32; // distributing into 32 buckets

    RowKeyDistributor keyDistributor =

                           new RowKeyDistributorByOneBytePrefix(bucketsCount);

    for (int i = 0; i < 100; i++) {

      Put put = new Put(keyDistributor.getDistributedKey(originalKey));

      ... // add values

      hTable.put(put);

    }

    

    

Scan을 진행하는 코드이다. 


       Scan scan = new Scan(startKey, stopKey);

    ResultScanner rs = DistributedScanner.create(hTable, scan, keyDistributor);

    for (Result current : rs) {

      ...

    }

    

    


Scan한 결과를 가지고 Map-Reduce를 진행할 수 있다.


      Configuration conf = HBaseConfiguration.create();

    Job job = new Job(conf, "testMapreduceJob");


    Scan scan = new Scan(startKey, stopKey);


    TableMapReduceUtil.initTableMapperJob("table", scan,

      RowCounterMapper.class, ImmutableBytesWritable.class, Result.class, job);


    // Substituting standard TableInputFormat which was set in

    // TableMapReduceUtil.initTableMapperJob(...)

    job.setInputFormatClass(WdTableInputFormat.class);

    keyDistributor.addInfo(job.getConfiguration());

    




HbaseWD의 실제 코드는 그리 많지는 않고.. 현재는 업데이트 되는 것 같지는 않다 ^^;


https://github.com/sematext/HBaseWD/tree/master/src/main/java/com/sematext/hbase/wd



Posted by '김용환'
,