HBase源码分析之KeyValue

在HBase存储数据的时候,单元格Cell的实现为KeyValue,每一个KeyValue都是一个低级的字节数组,它允许零赋值访问数据。
KeyValue的数据布局由Key Length(键长度)、Value Length(值长度)、Key、Value四大部分组成。其中,Key又由Row Length、Row、Column Family Length、Column Family、Column Qualifier、Time Stamp、Key Type七部分组成。
下图显示了KeyValue所包含数据的布局。
这里写图片描述
从左到右

  • 1、 Key Length : 存储Key的长度,占4B;
  • 2、Value Length:存储Value的长度,占4B;
  • 3、Key : Row Length、Row、Column Family Length、Column Family组成;

    • 3.1、Row Length: 存储Row的长度,即RowKey实际数据所占的长度,占2B;
    • 3.2、Row: 存储Row实际内容,即RowKey,其大小为Row Length
    • 3.3、Column Family Length:存储列簇Column Family的长度,占1B;
    • 3.4、Column Family:存储Column Family实际内容,大小为Column Family Length;
    • 3.5、Column Qualifier:存储Column Qualifier对应的数据,既然key中其他所有字段的大小都知道了,整个key的大小也知道了,那么这个Column Qualifier大小也是明确的了,无需再存储其length;
    • 3.6、Time Stamp:存储时间戳Time Stamp,占8B;
    • 3.7、Key Type:存储Key类型Key Type,占1B,Type分为Put、Delete、DeleteColumn、DeleteFamilyVersion、DeleteFamily等类型,标记这个KeyValue的类型;
  • 4、 Value:存储单元格Cell对应的实际的值Value。

  /** Size of the key length field in bytes*/
  public static final int KEY_LENGTH_SIZE = Bytes.SIZEOF_INT;

  /** Size of the key type field in bytes */
  public static final int TYPE_SIZE = Bytes.SIZEOF_BYTE;

  /** Size of the row length field in bytes */
  public static final int ROW_LENGTH_SIZE = Bytes.SIZEOF_SHORT;

  /** Size of the family length field in bytes */
  public static final int FAMILY_LENGTH_SIZE = Bytes.SIZEOF_BYTE;

  /** Size of the timestamp field in bytes */
  public static final int TIMESTAMP_SIZE = Bytes.SIZEOF_LONG;

 /** Size of the timestamp field in bytes */
  public static final int TIMESTAMP_SIZE = Bytes.SIZEOF_LONG;

在KeyValue中,有三个十分重要的变量分别是:bytes字节数组(存储KeyValue的实际数据),offset(KeyValue的起始位置),length(KeyValue的数据长度)

  // KeyValue core instance fields.
  //KeyValue核心实例存储域。
  //包含KV的不可变字节数组,前面说到的所有结构都存在这个字节
  protected byte [] bytes = null;   // an immutable byte array that contains the KV
  //KeyValue在数组bytes的起始位置
  protected int offset = 0;  // offset into bytes buffer KV starts at
  //从起始位置开始的KV的长度。
  protected int length = 0;  // length of the KV starting from offset.

KeyValue内容是存储在byte[]数组bytes中的,它是一个不变的byte[]数组,而存储的起始位置与长度,则分别由offset和length标识。


1. 获取Key Length

getKeyLength()方法用于获取KeyValue的Key长度Key Length

  /**
   * @return Length of key portion.
   */
  public int getKeyLength() {
    // 从KeyValue底层实际存储数据的byte[]数组bytes中位置offset开始,获取一个int型数据,也就是4B  
    return Bytes.toInt(this.bytes, this.offset);
  }

说明:int存储在计算机中占32位,byte占8位,所以占4B


2. 获取Value Length

  /**
   * @return Value length
   */
  @Override
  public int getValueLength() {
     //从KeyValue底层byte[]数组bytes中从offset+4开始,获取一个int,即4B数据。
     //也就是说,Key Length后紧跟着4B数据为value length
    int vlength = Bytes.toInt(this.bytes, this.offset + Bytes.SIZEOF_INT);
    return vlength;
  }

3. Key起始位置

  /**
   * @return Key offset in backing buffer..
   */
  public int getKeyOffset() {
    return this.offset + ROW_OFFSET;
  }

  // ROW_OFFSET为Key Length、Value Length之后的位置,定义如下:
   // How far into the key the row starts at. First thing to read is the short
  // that says how long the row is.
  public static final int ROW_OFFSET =
    Bytes.SIZEOF_INT /*keylength*/ +
    Bytes.SIZEOF_INT /*valuelength*/;

getKeyOffset()方法用于获取KeyValue中Key的起始位置,它的取值为整个KeyValue的起始位置offset加上ROW_OFFSET,而ROW_OFFSET为Key Length和Value Length所占大小之和,这也就验证了Key Length和Value Length之后存储的是RowKey的长度。


4. Value起始位置

  /**
   * @return the value offset
   */
  @Override
  public int getValueOffset() {
     //Value的起始位置:key的起始位置+key的长度
    int voffset = getKeyOffset() + getKeyLength();
    return voffset;
  }

getValueOffset()方法用于获取KeyValue中Value的起始位置,它的值为通过getKeyOffset()方法获取的Key的起始位置,再加上通过getKeyLength()方法获取的Key的长度,这也就验证了KeyValue中继Key Length、Value Length、Key之后,就是Value


5. Row起始位置

    @Override
    public int getRowOffset() {
       //key的起始位置+2B,2B为存row length所占的字节长度
      return getKeyOffset() + Bytes.SIZEOF_SHORT;
    }

getRowOffset()方法用于获取KeyValue中Row的起始位置,它的取值为Key的起始位置再加2B,即Row Length之后就是Row,与上面所讲一致!


6. 获取Row

  /**
   * Primarily for use client-side.  Returns the row of this KeyValue in a new
   * byte array.<p>
   *
   * If server-side, use {@link #getBuffer()} with appropriate offsets and
   * lengths instead.
   * @return Row in a new byte array.
   */
  @Override
  @Deprecated // use CellUtil.getRowArray()
  public byte [] getRow() {
    return CellUtil.cloneRow(this);
  }

getRow()方法用于获取Row内容,通过CellUtil的cloneRow(),传入KeyValue的实例对象,返回一个byte[]

  public static byte[] cloneRow(Cell cell){
    byte[] output = new byte[cell.getRowLength()];
    //将row从cell中copy到字节数组output中
    copyRowTo(cell, output, 0);
    return output;
  }

从上面的方法中可以看到,先构造一个大小为Row Length的byte[] 数组output,这就意味着Row的大小是由Row Length对应的值确定的。然后调用copyRowTo()方法,将KeyValue的bytes字节数组中存储Row内容的相应的字节内容copy到output字节数组中

  public static int copyRowTo(Cell cell, byte[] destination, int destinationOffset) {
  //将cell即KeyValue中byte[]数组bytes,从row开始处拷贝,
  //拷贝的数据长度为row length,刚好填满destination
    System.arraycopy(cell.getRowArray(), cell.getRowOffset(), destination, destinationOffset,
      cell.getRowLength());
    return destinationOffset + cell.getRowLength();
  }

System.arraycopy说明:

public static void arraycopy(Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)
src:源数组;    srcPos:源数组要复制的起始位置;
dest:目的数组;  destPos:目的数组放置的起始位置;    length:复制的长度。

7. Family起始位置

  /**
   * @return Family offset
   */
  @Override
  public int getFamilyOffset() {
    return getFamilyOffset(getRowLength());
  }

  /**
   * @return Family offset
   */
  private int getFamilyOffset(int rlength) {
      //整个keyValue的起始位置offset+ROW_OFFSET(KeyLength+ValueLength)+实际Row所占的大小rlength +1B(Family Length)
    return this.offset + ROW_OFFSET + Bytes.SIZEOF_SHORT + rlength + Bytes.SIZEOF_BYTE;
  }

Family起始位置整: 个KeyValue起始位置offset,加上ROW_OFFSET,也就是Key Length、Value Length所占大小,然后再加上Row Length所占大小2B,和通过getRowLength()方法获取的实际Row大小rlength,最后加上1B,即Family Length所占大小。
说明了:Key中ROw Length、Row之后就是Family Length和Family,而Family Length大小占1B。


8. 获取Family Length

  /**
   * @return Family length
   */
  @Override
  public byte getFamilyLength() {
    return getFamilyLength(getFamilyOffset());
  }

  /**
   * @return Family length
   */
  public byte getFamilyLength(int foffset) {
     //family起始位置减1,bytes[foffset-1]这1B数据就是family length
    return this.bytes[foffset-1];
  }

Family的长度用getFamilyLength()方法获取,通过由getFamilyOffset()方法获取的Family位置减1来获取的,与上面得到的验证一致,Family前面1B的数据就是Family Length信息。


9. Qualifier起始位置

  /**
   * @return Qualifier offset
   */
  @Override
  public int getQualifierOffset() {
    return getQualifierOffset(getFamilyOffset());
  }

  /**
   * @return Qualifier offset
   */
  private int getQualifierOffset(int foffset) {
      //Family的起始位置+Family的长度
    return foffset + getFamilyLength(foffset);
  }

getQualifierOffset()方法用于获取KeyValue中Qualifier的起始位置,它是通过Family的起始位置再加上Family的长度Family Length,说明了Family后就是Qualifier

10. Qualifier长度

  @Override
  public int getQualifierLength() {
    return getQualifierLength(getRowLength(),getFamilyLength());
  }

  private int getQualifierLength(int rlength, int flength) {
    //Key长度减去Row长度,Family长度,Row Length长度,Family Length长度,
    //Time Stamp长度,Key Type长度
    return getKeyLength() - (int) getKeyDataStructureSize(rlength, flength, 0);
  }
 //@return the key data structure length
  public static long getKeyDataStructureSize(int rlength, int flength, int qlength) {
    return KeyValue.KEY_INFRASTRUCTURE_SIZE + rlength + flength + qlength;
  }

KEY_INFRASTRUCTURE_SIZE 说明

public static final int KEY_INFRASTRUCTURE_SIZE = ROW_LENGTH_SIZE
      + FAMILY_LENGTH_SIZE + TIMESTAMP_TYPE_SIZE;
   TIMESTAMP_TYPE_SIZE说明
  // Size of the timestamp and type byte on end of a key -- a long + a byte.
  public static final int TIMESTAMP_TYPE_SIZE = TIMESTAMP_SIZE + TYPE_SIZE;

getQualifierLength()方法,用于获取KeyValue中Qualifier长度
Qualifier长度:为Key长度减去Row长度、Family长度、Row Length长度、Family Length长度、Time Stamp长度、Key Type长度的和。


11. Timestamp起始位置

  //@return Timestamp offset
  public int getTimestampOffset() {
    return getTimestampOffset(getKeyLength());
  }
  /**
   * @param keylength Pass if you have it to save on a int creation.
   * @return Timestamp offset
   */
  private int getTimestampOffset(final int keylength) {
    //Key的起始位置加上Key的长度,再减去Time Stamp和Key Type所占大小
    return getKeyOffset() + keylength - TIMESTAMP_TYPE_SIZE;
  }

Time Stamp的起始位置:是通过Key的起始位置加上Key的长度,再减去Time Stamp和Key Type所占大小来计算得到的。
说明:在Key中,Time Stamp处于倒数第二个位置,也就是在Qualifier之后,在Key Type之前,而Key Type则居于最后。


12. 获取TimeStamp

  /**
   *
   * @return Timestamp
   */
  @Override
  public long getTimestamp() {
    return getTimestamp(getKeyLength());
  }

  /**
   * @param keylength Pass if you have it to save on a int creation.
   * @return Timestamp
   */
  long getTimestamp(final int keylength) {
  //获取TimeStamp起始位置tsOffset  
    int tsOffset = getTimestampOffset(keylength);
    //从bytes中tsOffset位置开始读取一个Long,即8B
    return Bytes.toLong(this.bytes, tsOffset);
  }

说明: Long类型数据存储在计算机占64位,byte占8位,所以Timestamp占8B
所以:与上面提到的TimeStamp占8B是一致的

13. 获取Key Type

  /**
   * @return Type of this KeyValue.
   */
  @Deprecated
  public byte getType() {
    return getTypeByte();
  }

  /**
   * @return KeyValue.TYPE byte representation
   */
  @Override
  public byte getTypeByte() {
// 整个KeyValue的开始位置 + Key长度 - 1 + Key Length所占长度和Value Length所占长度和  
// 即Key Type位于整个Key的最后一个1B  
    return this.bytes[this.offset + getKeyLength() - 1 + ROW_OFFSET];
  }

Key Type通过getType()h和getTypeByte()方法来获取,是通过在bytes中,从整个KeyValue的位置offset + Key长度 - 1 + Key Length所占长度和Value Length所占长度和位置处获取的一个Byte来得到的,即Key Type位于整个Key的最后一个1B,这与上面所述也是一致的。


14. 获取Value值

 /**
   * Returns value in a new byte array.
   * Primarily for use client-side. If server-side, use
   * {@link #getBuffer()} with appropriate offsets and lengths instead to
   * save on allocations.
   * @return Value in a new byte array.
   */
  @Override
  @Deprecated // use CellUtil.getValueArray()
  public byte [] getValue() {
    return CellUtil.cloneValue(this);
  }
//说明:CellUtil的cloneValue方法
  public static byte[] cloneValue(Cell cell){
    byte[] output = new byte[cell.getValueLength()];
    copyValueTo(cell, output, 0);
    return output;
  }
//copyValueTo方法
    public static int copyValueTo(Cell cell, byte[] destination, int destinationOffset) {
    System.arraycopy(cell.getValueArray(), cell.getValueOffset(), destination, destinationOffset,
        cell.getValueLength());
    return destinationOffset + cell.getValueLength();
  }
   //说明:上面的cell.getValueLength()
  //@return Value length
  @Override
  public int getValueLength() {
    int vlength = Bytes.toInt(this.bytes, this.offset + Bytes.SIZEOF_INT);
    return vlength;
  }

将KeyValue中的bytes数组中对应的存储Value的字节数据copy到output字节数组中,从而返回output字节数组中的数据就是想要的Value的数据。
取Value的过程和取Key的过程是一样的。可以对比着看。

参考书籍:《HBase权威指南》
所读源码版本为:HBase1.16版本

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值