HBase API的刪除數據操作的分析


  使用HBase API刪除數據的時候需要注意的地方有很多,需要分成幾種情況進行分別的討論,進行刪除操作之前,首先需要構建刪除對象,即org.apache.hadoop.hbase.client包下的Delete,然后根據實際情況進行具體的操作,下面一一介紹:

  1、只傳rowKey

  rowKey這個參數可以在構建Delete對象的時候,作為構造方法的參數傳進去。這種情況相當於HBase Shell操作下的deleteall命令,會將指定rowKey下的所有列族以及所有列的所有版本數據都刪除,最終做的標記類型也是DeleteFamily。示例如下,我們客戶端,只傳rowKey值1004,調用API執行刪除操作。

  刪除之前:

  刪除之后:

  會發現,rowKey=1004的所有列族的數據都被刪除了,其中刪除所做的標記類型為DeleteFamily

API的代碼如下:

public static void deleteData(String tableName,String rowKey,String cf,String cn) throws IOException {

        //1.獲取表對象
        Table table = connection.getTable(TableName.valueOf(tableName));

        //2.構建刪除對象
        Delete delete = new Delete(Bytes.toBytes(rowKey));

        //3.執行刪除操作
        table.delete(delete);

        //4.關閉連接
        table.close();
    }

  2、傳入rowKey和Column Family

  傳入rowKey和列族,則會將指定的rowKey和列族下的所有版本的數據都給刪除掉,案例實操:

  刪除之前,rowKey=1005的對應的數據有兩個列族info1和info2,同時info1下含有兩個列sex和addr,sex列下含有兩個版本的數據:

 

 

   調用API進行刪除,刪除操作執行之后:

   可以判斷,傳入rowKey和列族之后,會刪除指定的rowKey和列族下對應的所有列,以及列的所有版本的數據,同時刪除標記是DeleteFamily。對應的代碼如下:

public static void deleteData(String tableName,String rowKey,String cf,String cn) throws IOException {

        //1.獲取表對象
        Table table = connection.getTable(TableName.valueOf(tableName));

        //2.構建刪除對象
        Delete delete = new Delete(Bytes.toBytes(rowKey));

        //2.2刪除指定的列族
        delete.addFamily(Bytes.toBytes(cf));

        //3.執行刪除操作
        table.delete(delete);

        //4.關閉連接
        table.close();
    }

  3、傳入rowKey、列族以及列名

  在此種情況下,向Delete對象添加列名的方法有兩種:addColumn()和addColumns()。

  (1)addColumns()

  /**
   * Delete all versions of the specified column.
   * @param family family name
   * @param qualifier column qualifier
   * @return this for invocation chaining
   */
  public Delete addColumns(final byte [] family, final byte [] qualifier) {
    addColumns(family, qualifier, this.ts);
    return this;
  }

  觀察源碼的注釋可以知道,該方法的作用是刪除指定列的所有版本的數據,同時它還有一個重載的方法,多了一個參數,這個參數是時間戳參數作用是刪除所有小於等於指定列的指定時間戳的所有版本的數據

  /**
   * Delete all versions of the specified column with a timestamp less than
   * or equal to the specified timestamp.
   * @param family family name
   * @param qualifier column qualifier
   * @param timestamp maximum version timestamp
   * @return this for invocation chaining
   */
  public Delete addColumns(final byte [] family, final byte [] qualifier, final long timestamp) {
    if (timestamp < 0) {
      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
    }
    List<Cell> list = familyMap.get(family);
    if (list == null) {
      list = new ArrayList<Cell>();
    }
    list.add(new KeyValue(this.row, family, qualifier, timestamp,
        KeyValue.Type.DeleteColumn));
    familyMap.put(family, list);
    return this;
  }

實操一下:

  刪除之前,對於rowKey=1010這一行數據,其info2列族下,phone對應的列,含有兩個版本的數據:

  

   執行刪除操作:

 

   可以看到,所有版本的數據都被刪除了。這是沒有指定時間戳的情況,下面來看一下指定時間戳的情況,我們重新向1010這一行put兩個不同版本的數據:

   其中,時間戳最大的那個版本的數據的時間戳的值為1596987728940較小的值為1596987723619。我們插入列的時候,傳入一個介於以上兩個版本數據的時間戳值之間的時間戳1596987728940。然后執行刪除操作,結果如下:

   發現時間戳較大的那個數據還存在,但是小於傳入的時間戳值的那個數據被刪除了。

   該方法刪除數據的時候,標記的刪除類型也是DeleteFamily。代碼如下:

    public static void deleteData(String tableName,String rowKey,String cf,String cn) throws IOException {

        //1.獲取表對象
        Table table = connection.getTable(TableName.valueOf(tableName));

        //2.構建刪除對象
        Delete delete = new Delete(Bytes.toBytes(rowKey));
         
     delete.addColumns(Bytes.toBytes(cf),Bytes.toBytes(cn),1596987728939l);

        //3.執行刪除操作
        table.delete(delete);

        //4.關閉連接
        table.close();
    }

  (2)addColumn()

  該方法添加指定的列,然后執行刪除操作,是存在較大的爭議的,它的作用是刪除指定列的最新版本的那一條數據而非全部,同時也可以傳入要刪除的指定的時間戳。先來看一下它的源碼:

  /**
   * Delete the latest version of the specified column.
   * This is an expensive call in that on the server-side, it first does a
   * get to find the latest versions timestamp.  Then it adds a delete using
   * the fetched cells timestamp.
   * @param family family name
   * @param qualifier column qualifier
   * @return this for invocation chaining
   */
  public Delete addColumn(final byte [] family, final byte [] qualifier) {
    this.deleteColumn(family, qualifier, this.ts);
    return this;
  }

  帶有時間戳參數的源碼:

  /**
   * Delete the specified version of the specified column.
   * @param family family name
   * @param qualifier column qualifier
   * @param timestamp version timestamp
   * @return this for invocation chaining
   */
  public Delete addColumn(byte [] family, byte [] qualifier, long timestamp) {
    if (timestamp < 0) {
      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
    }
    List<Cell> list = familyMap.get(family);
    if(list == null) {
      list = new ArrayList<Cell>();
    }
    KeyValue kv = new KeyValue(this.row, family, qualifier, timestamp, KeyValue.Type.Delete);
    list.add(kv);
    familyMap.put(family, list);
    return this;
  }

  先來看一下注釋說的,第一個方法的功能就是刪除指定列的最新版本的數據,其中還說這個方法在服務器端是一個比較昂貴的調用,它會先執行get方法獲取最新版本的時間戳,然后將delete標記添加到這個最新的時間戳上。這就相當於標記了指定列最新版本的數據是被刪除的了。第二個方法會傳入一個時間戳參數,然后作用是刪除指定列的指定版本的數據。那我們來案例實操一下:

  我們先put兩條數據到表中,其中rowKey=1011,列族為info1,列名為name,列的版本數為1:

 

   執行完成之后,結果如下:

   會發現,時間戳最大的那個數據被刪除了,但是執行scan操作,原先時間戳較小的那個數據顯示出來了,其中刪除標記是Delete,刪除標記對應的時間戳正是原先最大的那個時間戳。這是在兩個put的數據都在內存中的時候所出現的情況,那我們再進一步進行一種實驗,調用API執行刪除操作放在put數據並進行刷寫到磁盤之后,我們再次put兩條數據,然后執行flush操作:

   調用API執行刪除操作,然后使用scan命令查看數據:

    刪除操作的api代碼:

    public static void deleteData(String tableName,String rowKey,String cf,String cn) throws IOException {

        //1.獲取表對象
        Table table = connection.getTable(TableName.valueOf(tableName));

        //2.構建刪除對象
        Delete delete = new Delete(Bytes.toBytes(rowKey));
        //2.1設置刪除的列
        delete.addColumn(Bytes.toBytes(cf),Bytes.toBytes(cn));


        //3.執行刪除操作
        table.delete(delete);

        //4.關閉連接
        table.close();
    }

  查詢結果:

  發現1012對應的數據沒了,也就是說在設計表的時候,某個列的最大版本數,被設置為了1,然后當我們多次對這個列put數據的時候,如果這些數據都還在內存中,那我們使用addColumn方法指定刪除的列族和列名的時候,會刪除時間戳最大的那一條數據,然后第二大的時間戳對應的那一條數據對顯示出來。如果在put多條數據和調用API刪除數據的過程之間發生了刷寫數據到磁盤的過程(自動或者手動),那么就不會產生上面的現象。

  當然,以上現象都是針對設計表的時候,最大版本數為1的情況,如果設計表的時候,設置了列的最大版本數大於1,那么put多條數據之后,然后刷寫到磁盤,使用addColumn方法指定列和列族刪除數據,最后進行scan掃描的時候還是會有這個列的其他版本的數據顯示出來。

  第二種重載的方法,傳入指定的時間戳,這個方法也只是將指定的時間戳標記為delete,如果指定列沒有對應時間戳的數據,則不會刪除任何數據,除非插入的數據的時間戳和指定的時間戳相同,否則是不會刪除數據。

  對於addColumn方法應該謹慎使用為好,因為會造成數據不一致的情況發生,比如我們對某個列put了多條數據,在刷寫之前,調用API進行了刪除操作,然后執行了刷寫,這是磁盤中保存的這一列的數據是時間戳較小的那個版本的數據。如果,put了多條數據,刷寫操作在刪除操作之前,那么最終磁盤中該列是沒有數據的(針對列的最大版本數為1的情況),這就造成了數據不一致的情況。

 

  一定要注意,這種方法,標記的時間戳不是專門為這次刪除操作生成的時間戳,而是先去獲取指定列的所有版本中最新版本的時間戳,然后將這個時間戳標記為delete,這是本質。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM