• 3장 클라이언트 API : 기본기능 - Delete 메서드

    2018. 8. 17. 19:53

    by. 위지원

     

    야호!! 드디어 삭제다!

    2018/08/10 - [2018년 하반기/HBASE] - 3장 클라이언트 API : 기본기능 - Put 메서드

    2018/08/17 - [2018년 하반기/HBASE] - 3장 클라이언트 API : 기본기능 - Get 메서드




    CRUD(Create,Read,Update,Delete) 기능

    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE# [위지원의 블로그]
    CRUD(Create,Read,Update,Delete) 기능

    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE# [위지원의 블로그]

    CRUD(Create,Read,Update,Delete) 기능


    2.Get 메서드



    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE [위지원의 블로그]




    CRUD(Create,Read,Update,Delete) 기능

    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE# [위지원의 블로그]
    CRUD(Create,Read,Update,Delete) 기능

    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE# [위지원의 블로그]

    CRUD(Create,Read,Update,Delete) 기능


    2.Get 메서드



    출처: http://weejw.tistory.com/category/2018년 하반기/HBASE [위지원의 블로그]

    CRUD(Create,Read,Update,Delete) 기능

    3.Delete 메서드


    Delete 라는 클래스를 이용해서 삭제를 수행할 수 있다. Delete 객체를 생성해서 테이블의 delete 메서드로 넘겨줄 수 있다.





    delete 객체를 생성하는 방법은 역시 여러가지가 있지만, 그 중에서 몇개만 살펴보자면 가장 기본인 row만 넘겨주는 경우와 timestamp까지 넘겨주는 경우가 있다.



    Delete class의 다음 메서드를 사용하면 row에서 삭제하고자 하는 데이터의 범위를 좁힐 수 있다.





    Table table = connection.getTable(TableName.valueOf("testtable"));

    Delete delete = new Delete(Bytes.toBytes("row1")); //특정 row를 지정한다.

    delete.setTimestamp(1); // row 삭제를 위한 TimeStemp를 지정한다.

    delete.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual1")); //컬럼하나의 모든 version을 삭제한다.
    delete.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual2"), 3); // 특정 version과 그 이전버전을 모두 삭제한다.
    //변경되었다. version이 아니라 TimeStemp를 넘겨줘야한다(API참조)
    delete.addFamily(Bytes.toBytes("colfam1")); //Family를 모두 삭제한다.
    delete.addFamily(Bytes.toBytes("colfam1"), 3); //특정 version과 그 이전 버전을 모두 삭제한다.

    table.delete(delete);


    위에 다양한 방법중에 특정 퀄리파이어를 삭제한 장면이다.


    이것은 컬럼 패밀리를 삭제해본 모습이다.


    version삭제가 안된다.. 한참을 고민한끝에 그것은 바로!! timestamp 값을 지정해서 값을 PUT! 해줘야한다는 것이였다..^^;; 지난번 배운 Put 메서드에서 보면 long ts로 timestemp를 지정해서 생성할 수 있었다. 해보자. (책을 너무 대충읽오 있나보다)



    일반으로 지정된 시간에 대해서 삭제하려고 하면 자바 API로 하면 아래와 같이 오류가 뜬다. 분명 아규먼트 타입상 Long의 자리인데..음..,,

    Delete delete = new Delete(Bytes.toBytes("row1"));

    delete.addColumns(Bytes.toBytes("colfam1"), Bytes.toBytes("qual2"), 1534501337711);

    같은 코드를 shell에서 외쳤을때는 전혀 문제가 없다..ㅠ_ㅠ 왜일까?


    Delete 리스트


    Delete 인스턴스의 리스트를 생성해서 한번에 삭제한다. 위에서 소개할때 두번째에 있던 친구이다.



    이번에는 바~로 코딩에 들어가보겠다.!!


    아래 코드를 이용하여서 비어있는 테이블에 데이터를 삽입해주자.

    Put put = new Put(Bytes.toBytes("row1"));

    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),2,Bytes.toBytes("val2"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),3,Bytes.toBytes("val3"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual2"),4,Bytes.toBytes("val4"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),5,Bytes.toBytes("val5"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),6,Bytes.toBytes("val6"));

    Put put2 = new Put(Bytes.toBytes("row2"));

    put2.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put2.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),2,Bytes.toBytes("val2"));
    put2.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),3,Bytes.toBytes("val3"));
    put2.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual2"),4,Bytes.toBytes("val4"));
    put2.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),5,Bytes.toBytes("val5"));
    put2.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),6,Bytes.toBytes("val6"));

    Put put3 = new Put(Bytes.toBytes("row3"));

    put3.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put3.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),2,Bytes.toBytes("val2"));
    put3.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),3,Bytes.toBytes("val3"));
    put3.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual2"),4,Bytes.toBytes("val4"));
    put3.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),5,Bytes.toBytes("val5"));
    put3.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),6,Bytes.toBytes("val6"));


    Table table = connection.getTable(TableName.valueOf("testtable"));
    table.put(put);
    table.put(put2);
    table.put(put3);

    hbase(main):006:0> scan 'testtable',{VERSIONS=>5}
    ROW                     COLUMN+CELL                                                       
     row1                   column=colfam1:qual1, timestamp=2, value=val2                     
     row1                   column=colfam1:qual1, timestamp=1, value=val1                     
     row1                   column=colfam1:qual2, timestamp=3, value=val3                     
     row1                   column=colfam2:qual2, timestamp=4, value=val4                     
     row1                   column=colfam2:qual3, timestamp=6, value=val6                     
     row1                   column=colfam2:qual3, timestamp=5, value=val5                     
     row2                   column=colfam1:qual1, timestamp=2, value=val2                     
     row2                   column=colfam1:qual1, timestamp=1, value=val1                     
     row2                   column=colfam1:qual2, timestamp=3, value=val3                     
     row2                   column=colfam2:qual2, timestamp=4, value=val4                     
     row2                   column=colfam2:qual3, timestamp=6, value=val6                     
     row2                   column=colfam2:qual3, timestamp=5, value=val5                     
     row3                   column=colfam1:qual1, timestamp=1, value=val1                     
     row3                   column=colfam1:qual2, timestamp=3, value=val3                     
     row3                   column=colfam1:qual2, timestamp=2, value=val2                     
     row3                   column=colfam2:qual2, timestamp=4, value=val4                     
     row3                   column=colfam2:qual3, timestamp=6, value=val6                     
     row3                   column=colfam2:qual3, timestamp=5, value=val5                     
    3 row(s)
    Took 0.1618 seconds 


    그리고 나서 Delete List를 생성하고 그안에 delete 객체를 생성해서 add()한 뒤 삭제해주자.


    List<Delete> deletes = new ArrayList<Delete>();

    Delete delete1 = new Delete(Bytes.toBytes("row1"));
    delete1.setTimestamp(4); //row1에서 timestemp가 4인것과 그것보다 낮은것들을 삭제한다.
    deletes.add(delete1);

    Delete delete2 = new Delete(Bytes.toBytes("row2"));
    delete2.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual1")); //row2에서 qual1의 최신버전 1개 삭제
    delete2.addColumns(Bytes.toBytes("colfam2"), Bytes.toBytes("qual3"), 5); //qual3중에 timestemp가 5인것
    deletes.add(delete2);

    Delete delete3 = new Delete(Bytes.toBytes("row3"));
    delete3.addFamily(Bytes.toBytes("colfam1")); //row3의 colfam1을 삭제한다.
    delete3.addFamily(Bytes.toBytes("colfam2"), 3); //row3의 colfam2중에서 timestemp가 3인것과 그보다 낮은 것을 삭제한다.
    deletes.add(delete3);

    table.delete(deletes);

    hbase(main):007:0> scan 'testtable',{VERSIONS=>5}
    ROW                    COLUMN+CELL                                                       
     row1                   column=colfam2:qual3, timestamp=6, value=val6                     
     row1                   column=colfam2:qual3, timestamp=5, value=val5                     
     row2                   column=colfam1:qual1, timestamp=1, value=val1                     
     row2                   column=colfam1:qual2, timestamp=3, value=val3                     
     row2                   column=colfam2:qual2, timestamp=4, value=val4                     
     row2                   column=colfam2:qual3, timestamp=6, value=val6                     
     row3                   column=colfam2:qual2, timestamp=4, value=val4                     
     row3                   column=colfam2:qual3, timestamp=6, value=val6                     
     row3                   column=colfam2:qual3, timestamp=5, value=val5                     
    3 row(s)
    Took 0.0964 seconds


    위에 메소드에다가 버그를 하나 심어주고 확인을 해보자.


    Delete delete4 = new Delete(Bytes.toBytes("row2"));
    delete4.addColumn(Bytes.toBytes("BOGUS"),Bytes.toBytes("qual1"));
    deletes.add(delete4);

    try {
    table.delete(deletes);
    } catch (Exception e) {
    System.err.println("Error: " + e);
    }
    table.close();

    System.out.println("Deletes length: " + deletes.size()); //리스트에 남아있는 친구가 있는지 확인을 해보자
    for (Delete delete : deletes) {
    System.out.println(delete);
    }

    그럼 아래와 같이 굉장히 길게 에러가 출력되는데 중요한건 가장 마지막에 있는 단 하나! 리스트에는 delete 객체가 단하나! 남아있다.


    Error: org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException: Failed 1 action:

    org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException: Column family BOGUS does not exist in region testtable,,1534504182114.f9ed523be3cbf3a9902c1b7718d08937. in table 'testtable', {NAME => 'colfam1', VERSIONS => '10', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}, {NAME => 'colfam2', VERSIONS => '10', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
            at org.apache.hadoop.hbase.regionserver.RSRpcServices.doBatchOp(RSRpcServices.java:1038)
            at org.apache.hadoop.hbase.regionserver.RSRpcServices.doNonAtomicBatchOp(RSRpcServices.java:961)
            at org.apache.hadoop.hbase.regionserver.RSRpcServices.doNonAtomicRegionMutation(RSRpcServices.java:924)
            at org.apache.hadoop.hbase.regionserver.RSRpcServices.multi(RSRpcServices.java:2673)
            at org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:42014)
            at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:409)
            at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:130)
            at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:324)
            at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:304)
    : 1 time, servers with issues: netdb.slave2.com,16020,1534483987538
    Deletes length: 1
    {"totalColumns":1,"row":"row2","families":{"BOGUS":[{"qualifier":"qual1","vlen":0,"tag":[],"timestamp":9223372036854775807}]},"ts":9223372036854775807}



    원자적 '확인 후 삭제' 연산


    힘들어죽겠다.자 다음은 원자적 확인후 삭제이다. Put에서 한것처럼 확인을 먼저한다음에 라는 전제 조건이 붙은 것 뿐이다.

    table class의 API를 확인하니 오호호호호 Deprecated... checkAndMutate를 대신 애용해달라는 말이 보인다. 일단 checkAndDelete예제부터 해보자.





    Put put = new Put(Bytes.toBytes("row1"));

    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),2,Bytes.toBytes("val2"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual3"),3,Bytes.toBytes("val3"));

    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual2"),2,Bytes.toBytes("val2"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),3,Bytes.toBytes("val3"));


    table.put(put); //새로운 값들을 삽입하고

    Delete delete1 = new Delete(Bytes.toBytes("row1"));
    delete1.addColumns(Bytes.toBytes("colfam1"), Bytes.toBytes("qual3"));

    boolean res1 = table.checkAndDelete(Bytes.toBytes("row1"),
    Bytes.toBytes("colfam2"), Bytes.toBytes("qual3"), null, delete1); //앞에 조건이 맞으면(null이라는) 마지막 delete1 연산을 수행한다.
    System.out.println("Delete 1 successful: " + res1);

    Delete delete2 = new Delete(Bytes.toBytes("row1"));
    delete2.addColumns(Bytes.toBytes("colfam2"), Bytes.toBytes("qual3")); //삭제한다.
    table.delete(delete2);

    boolean res2 = table.checkAndDelete(Bytes.toBytes("row1"),
    Bytes.toBytes("colfam2"), Bytes.toBytes("qual3"), null, delete1); //바로 위에서 삭제했기때문에 value부분에 전달한 파라미터 null과 조건이 맞기때문에 이번에는 true가 출력된다.

    * delete연산은 맞는게 없으면 아무것도 수행하지 않는다.
    System.out.println("Delete 2 successful: " + res2);

    Delete delete3 = new Delete(Bytes.toBytes("row2"));
    delete3.addFamily(Bytes.toBytes("colfam1"));

    try{
    boolean res4 = table.checkAndDelete(Bytes.toBytes("row1"),
    Bytes.toBytes("colfam1"), Bytes.toBytes("qual1"),
    Bytes.toBytes("val1"), delete3);
    System.out.println("Delete 3 successful: " + res4);
    } catch (Exception e) {
    System.err.println("Error: " + e.getMessage()); //아래와 같이 에러가 발생한다. 앞에 조건은 부합하지만 delete3에서는 존재하지 않는 row2에 대해 언급했기때문이다.
    }

    hbase(main):028:0> scan 'testtable'
    ROW                   COLUMN+CELL                                              
     row1                 column=colfam1:qual1, timestamp=1, value=val1            
     row1                 column=colfam1:qual2, timestamp=2, value=val2            
     row1                 column=colfam2:qual1, timestamp=1, value=val1            
     row1                 column=colfam2:qual2, timestamp=2, value=val2            
    1 row(s)
    Took 0.0417 seconds


    Delete 1 successful: false
    Delete 2 successful: true
    Error: Failed 1 action: org.apache.hadoop.hbase.DoNotRetryIOException: Action's getRow must match
            at org.apache.hadoop.hbase.regionserver.HRegion.checkMutationType(HRegion.java:4149)
            at org.apache.hadoop.hbase.regionserver.HRegion.checkAndMutate(HRegion.java:4029)
            at org.apache.hadoop.hbase.regionserver.RSRpcServices.mutate(RSRpcServices.java:2837)
            at org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:42000)
            at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:409)
            at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:130)
            at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:324)
            at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:304)
    : 1 time, servers with issues: netdb.slave1.com,16020,1534483984776


    이번에는  checkandMutate를 실험해보자. 아래 코드를 보면 RowMutations라는 객체를 생성해서 확인을 하고 있다.


    import org.apache.hadoop.hbase.filter.CompareFilter;


    Table table = connection.getTable(TableName.valueOf("testtable"));

    Put put = new Put(Bytes.toBytes("row1"));

    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual2"),2,Bytes.toBytes("val2"));
    put.addColumn(Bytes.toBytes("colfam1"),Bytes.toBytes("qual3"),3,Bytes.toBytes("val3"));

    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual1"),1,Bytes.toBytes("val1"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual2"),2,Bytes.toBytes("val2"));
    put.addColumn(Bytes.toBytes("colfam2"),Bytes.toBytes("qual3"),3,Bytes.toBytes("val3"));

    table.put(put);

    Put put2 = new Put(Bytes.toBytes("row1"));
    put2.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual1"),
    4, Bytes.toBytes("val99"));
    put2.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual4"),
    4, Bytes.toBytes("val100"));

    Delete delete = new Delete(Bytes.toBytes("row1"));
    delete.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual2"));

    RowMutations mutations = new RowMutations(Bytes.toBytes("row1"));
    mutations.add(put2);
    mutations.add(delete);

    boolean res1 = table.checkAndMutate(Bytes.toBytes("row1"),
    Bytes.toBytes("colfam2"), Bytes.toBytes("qual1"),
    CompareFilter.CompareOp.LESS, Bytes.toBytes("val1"), mutations);
    System.out.println("Mutate 1 successful: " + res1); row1 colfam2:qual1 value의 값(1)과 val1과 비교한다. 더 작지 않기때문에 false

    Put put3 = new Put(Bytes.toBytes("row1"));
    put3.addColumn(Bytes.toBytes("colfam2"), Bytes.toBytes("qual1"), //row1 colfam2:qual1 value 값을 2로 변경한다.
    4, Bytes.toBytes("val2"));
    table.put(put3);

    boolean res2 = table.checkAndMutate(Bytes.toBytes("row1"),
    Bytes.toBytes("colfam2"), Bytes.toBytes("qual1"), 비교하면 row1 colfam2:qual1의 값(2)보다 val1이 더작다 true
    CompareFilter.CompareOp.LESS, Bytes.toBytes("val1"), mutations); //true이기때문에 mutations의 OP가 수행된다.
    System.out.println("Mutate 2 successful: " + res2);

    Mutate 1 successful: false
    Mutate 2 successful: true


    hbase(main):007:0> scan 'testtable'
    ROW                   COLUMN+CELL                                              
     row1                 column=colfam1:qual1, timestamp=4, value=val99           
     row1                 column=colfam1:qual3, timestamp=3, value=val3            
     row1                 column=colfam1:qual4, timestamp=4, value=val100          
     row1                 column=colfam2:qual1, timestamp=4, value=val2            
     row1                 column=colfam2:qual2, timestamp=2, value=val2            
     row1                 column=colfam2:qual3, timestamp=3, value=val3            
    1 row(s)
    Took 0.0747 seconds