Hbase 安装与基本使用


单点模式
1 选择Hbase版本要与hadoop版本相对应.
下载地址:http://mirrors.cnnic.cn/apache/hbase
 
2 安装,解压下载的tar文件
3 配置conf/hbase-site.xml 去配置hbase.rootdir,来选择Hbase将数据写到哪个目录
单机配置,只需要如下配置hbase-site.xml:
<property>
     <name>hbase.rootdir</name>
     <value> file:///home/grid/hbase/data </value>
     <description>The directory sharedbyRegionServers.  </description>
  </property>
单击配置:<value>file:///home/grid/hbase/data</value>

4 修改hbase-env.sh :需要修改 JAVA_HOME
5 启动HBase
$ ./bin/start-hbase.sh

6  运行java  :jps命令 出现进程HMaster
7  bin/hbase shell 命令 出现版本号  quit 退出 单机版安装成功


伪分布模式
单点模式基础上
编辑hbase-env.sh 增加HBASE_CLASSPATH环境变量,值为hadoop配置文件的目录
HBASE_CLASSPATH=/web/hadoop/conf
编辑hbase-site.xml打开分布模式
   <configuration>
   <property>
     <name>hbase.rootdir</name>
     <value>hdfs://master:9000/hbase</value>
     <description>The directory sharedbyRegionServers.habase数据存储位置改为hadoop中  </description>
  </property>
//设置zookeeper的主机, localhost在host中的解析一定要有
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>localhost</value>
  </property>
//开启分布式模式
    <property>
         <name>hbase.cluster.distributed</name>
         <value>true</value>
    </property>
</configuration>
覆盖hadoop核心jar包(不同版本不一样)
启动hbase 
./start-hbase.sh
验证启动
1) 运行jps命令看进程 多了下面三个进程 HRegionServer、HQuorumPeer、HMaster
2) http://192.168.102.136:60010/master.jsp,查看hbase运行情况


HBASE 中shell使用
输入错误用ctrl+del 或 ctrl+backspace 可以删除
进入shell命令界面: Hbase shell
整个学是在网上搜到的,照着整个的操作一遍,作为学习记录:
来自:http://www.cnblogs.com/linjiqin/archive/2013/03/08/2949339.html




学生成绩表作为例子:
 
一、 列与列簇理解:
这里 grad 对于表来说是一个列,course 对于表来说是一个列族,这个列族由三个列组成 china、math 和 english,当然我们可以根据我们的需要在 course 中建立更多的列族,如computer,physics 等相应的列添加入 course 列族。(备注:列族下面的列也是可以没有名字的。
二、 Hbase中没有数据类型,所有数据都是字符类型。
三、 时间戳系统会自动创建,也可强行插入
四、 官网API:http://hbase.apache.org/apidocs/index.html
五、 命令使用
1) Create命令
创建一个具有两个列簇“grad”和“course”的表“scores”。其中表名、行和列都要用单引号括起来并用逗号隔开,
create 'scores', 'name', 'grad', 'course'
2) list命令
查看当前HBase中具有那些表,> list
3) Describe 命令
查看表“scores”的构造   >describe ‘scores’
4) Put命令
使用 put 命令向表中插入数据,参数分别为表名、行名(行健唯一)、列名和值,其中列名前需要列族最为前缀,时间戳由系统自动生成。
格式: put 表名,行名,列名([列族:列名]),值
例子:hbase(main):012:0> put 'scores', 'xiapi', 'grad:', '1'
hbase(main):012:0> put 'scores', 'xiapi', 'grad:', '2' --修改操作(update)
b. 给“xiapi”这一行的数据的列族“course”添加一列“<china,97>”。
hbase(main):012:0> put 'scores', 'xiapi',  'course:china', '97'
hbase(main):012:0> put 'scores', 'xiapi',  'course:math', '128'
hbase(main):012:0> put 'scores', 'xiapi',  'course:english', '85'
5) get 命令
a.查看表“scores”中的行“xiapi”的相关数据。
hbase(main):012:0> get 'scores', 'xiapi'
b.查看表“scores”中行“xiapi”列“course :math”的值。
hbase(main):012:0> get 'scores', 'xiapi', 'course :math'
或者
hbase(main):012:0> get 'scores', 'xiapi', {COLUMN=>'course:math'}
hbase(main):012:0> get 'scores', 'xiapi', {COLUMNS=>'course:math'}
备注:COLUMN 和 COLUMNS 是不同的,scan 操作中的 COLUMNS 指定的是表的列族, get操作中的 COLUMN 指定的是特定的列,COLUMNS 的值实质上为“列族:列修饰符”。
6) scan 命令
a. 查看表“scores”中的所有数据。
hbase(main):012:0> scan 'scores'
注意:
scan 命令可以指定 startrow,stoprow 来 scan 多个 row。
例如:
scan 'user_test',{COLUMNS =>'info:username',LIMIT =>10, STARTROW => 'test', STOPROW=>'test2'}
b.查看表“scores”中列族“course”的所有数据。
hbase(main):012:0> scan  'scores', {COLUMN => 'grad'}
hbase(main):012:0> scan  'scores', {COLUMN=>'course:math'}
hbase(main):012:0> scan  'scores', {COLUMNS => 'course'}
hbase(main):012:0> scan  'scores', {COLUMNS => 'course'}
7) count 命令
hbase(main):068:0> count 'scores'
8) exists 命令
hbase(main):071:0> exists 'scores'
9) delete 命令
删除表“scores”中行为“xiaoxue”, 列族“course”中的“math”。
hbase(main):012:0>  delete 'scores', 'xiapi', 'course:math'
10) truncate 命令
hbase(main):012:0>  truncate 'scores'
11) disbale、drop 命令
通过“disable”和“drop”命令删除“scores”表。
hbase(main):012:0>  disable 'scores' --enable 'scores'
hbase(main):012:0>  drop 'scores'

12)   status命令
hbase(main):072:0> status

13) version命令
hbase(main):073:0> version

六、 什么情况下使用Hbase

1) 数据查询模式已经确定,且不易改变,就是说hbase使用在某种种特定的情况下,且不能变动。
2) 告诉插入,大量读取。因为分布式系统对大量数据的存取更具优势。
3) 尽量少的有数据修改。因为hbase中的数据修改知识在后面添加一行新数据,表示覆盖前一条,大量修改浪费大量空间。(hbase基于hdfs存储不支持修改)
注意:
a) 没有辅助索引,可以通过新建另一张表作为辅助索引,查找索引,然后查询需要的数据
b) “复合行健”实现多条件查询

七、 hbase的优势:
有时间戳,适合告诉时间查询;
基于行健的查询异常快(行健可参考后面hbase的表结构),特别是最近的数据可能还在memstore里,没有io开销;
分布式处理,可以方便的添加节点。

九、hbase优化,插入和查询优化

 1)快速录入数据,预分区(多个分区才可以进行并行处理)

     录入数据的时候hbase默认只有一个分区,当这个分区达到一定程度的时候会split成另一个分区,这个split会比较慢,所以提前创建分区,如果数据录入的时候会根据rowkey进行比对

录入到相应的分区中。

2 )如果用默认的分区策略,则rowkey的开头要散列,这样数据才能落入到不同的分区。

       或者用自定义的预分区,则数据会按照规则落入到自己定义的分区中,防止数据在同一个分区一直操作。

3)批量提交数据,数据到达一定大小才提交

    

        //将数据自动提交功能关闭
        table.setAutoFlushTo(false);
        //设置数据缓存区域
        table.setWriteBufferSize(64 * 1024 * 1024);
        put.setWriteToWAL(false); //导数据的时候为了加快速度可以,取消写日志
        //刷新缓存区
        table.flushCommits();
        table.close();
 4)数据查询或者添加的时候要创建表的连接池HTable连接池

 

5)查询不要用scan,可以用ES+Hbase替代scan

6)查看表某个表的所有分区的文件:

   hadoop fs -ls -R  /apps/hbase/data/data//default/qixiang1

   检查hbase某个region的大小,单位字节: hadoop fs -du /hbase/your_table

十、我测试的rowkey生成策略,解决预分区的问题:

     1)我的数据中有个站点,且每天每个小时一个站点一条数据则rowkey为   站点号+2位的小时数字 

     2)不同的年月日的数据在1中的rowkey中后面添加年月日,则rowkey肯定唯一

     3)计算预分区的时候用一天的数据的所有rowkey比对进行预分区,则不同年月日的数据一样也会平均的落入这个预分区中,就解决了预分区的问题。

 

十一、随机rowkey生成策略代码

  

package com.jusfoun.spark.util;

import java.io.Serializable;
import java.util.LinkedHashSet;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.MD5Hash;
 
//import com.kktest.hbase.BitUtils;
/**
*
*
**/
public class HashRowKeyGenerator implements RowKeyGenerator , Serializable {
    private static long currentId = 1;
    private static long currentTime = System.currentTimeMillis();

    public static void main(String[] args) {
    	HashRowKeyGenerator  d = new HashRowKeyGenerator() ; 
    	for(int i=0;i<10;i++){
    		System.out.println( d.nextId()  );
    	}

//    	byte[] bs = toBytes(1482982753743l ) ;
//    	long l = toLong(bs, 0, bs.length); 
//    	System.out.println( l );
    	
	}
    
//    //每次得到的结果左移动8位,然后后面的字节依次拼接到后面
//    public static long toLong(byte[] bytes, int offset, final int length) {
//        long l = 0;
//        for(int i = offset; i < offset + length; i++) {
//          //每次计算的结果 向左移动8位,向左移动8位后低8位全为0
//          l <<= 8;
//          System.out.println( Long.toBinaryString( l )  );
//          //上面计算的结果& 0xFF后得到高于8位全为0的结果
//          long byteValue =  bytes[i] & 0xFF ;
//          System.out.println(  Long.toBinaryString( byteValue )    );
//          //当前字节结算后的结果,用异或拼接的l的低8位上面,因为l当前值的低8位全都是0,则异或后l低8位就变成了byteValue的值
//          //异或运算符,相应位的值相同的,结果为 0,不相同的结果为 1。
//          l ^= byteValue;
//          System.out.println( Long.toBinaryString( l )  );
//          System.out.println(  );
//        }
//        return l;
//      }
    
//    //每次截取8位,然后左移8,
//    public static byte[] toBytes(long val) {
////    	System.out.println( "原来的长整形数据:"+val );
//        byte [] b = new byte[8];
//        for (int i = 7; i > 0; i--) {
//          //强制转型,后留下长整形的低8位
//          b[i] = (byte) val;
//          String str = Long.toBinaryString( val) ;
//          String lb = Long.toBinaryString( b[i] ) ;
//          String lb2 = Long.toBinaryString( b[i]&0xff ) ;
//
//
//          System.out.println("转换为字节:"+ str );
//          System.out.println( lb );
//          System.out.println( lb2 );
//          //向右移动8位,则第二次循环则计算第二个8位数
//          val >>>= 8;
//        }
//        b[0] = (byte) val;
//        return b;
//      }


    /**
     * 每次生成一个rowkey
     * 并发的情况下可能存在重复
     * @return
     */
    public byte[] nextId()
    {
        try {
//        	System.out.println( Long.MAX_VALUE - currentTime );

            currentTime = getRowKeyResult(Long.MAX_VALUE - currentTime);
            byte[] currentTimeBytes = Bytes.toBytes(currentTime);
            byte[] lowT = Bytes.copy(currentTimeBytes, 4, 4);
            byte[] currentIdBytes = Bytes.toBytes(currentId);
            byte[] lowU = Bytes.copy(currentIdBytes, 4, 4);
            byte[] result = Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowT, lowU))
                    .substring(0, 8).getBytes(), Bytes.toBytes(currentId));
            return  Bytes.toBytes( Bytes.toLong( result)+"") ;//返回 字符串的字节数组
        } finally {
            currentId++;
        }
    }
    //测试方法
    public byte[] nextIdTest()
    {
        try {
            System.out.println("当前的rowkey == "+currentId);
            return     Bytes.toBytes(currentId+"");
        } finally {
            currentId++;
        }
    }

    //得到一个字符串类型的rowkey
    public String nextIdString()
    {
        try {
//        	System.out.println( Long.MAX_VALUE - currentTime );

            currentTime = getRowKeyResult(Long.MAX_VALUE - currentTime);
            byte[] currentTimeBytes = Bytes.toBytes(currentTime);
            byte[] lowT = Bytes.copy(currentTimeBytes, 4, 4);
            byte[] currentIdBytes = Bytes.toBytes(currentId);
            byte[] lowU = Bytes.copy(currentIdBytes, 4, 4);
            byte[] result = Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowT, lowU))
                    .substring(0, 8).getBytes(), Bytes.toBytes(currentId));
            return Bytes.toLong( result)+"";
        } finally {
            currentId++;
        }
    }

    /**
     *  getRowKeyResult
     * @param tmpData
     * @return
     */
    public static long getRowKeyResult(long tmpData)
    {
        String str = String.valueOf(tmpData);
        StringBuffer sb = new StringBuffer();
        char[] charStr = str.toCharArray();
        for (int i = charStr.length -1 ; i > 0; i--)
        {
            sb.append(charStr[i]);
        }
        
        return Long.parseLong(sb.toString());
    }
}

 2)随机数预分区数组代码

  

package com.jusfoun.spark.util;

import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.TreeSet;

import org.apache.hadoop.hbase.util.Bytes;

/**
 * 
 * @author kuang hj
 *
 */
public class HashChoreWoker{
    // 随机取机数目
    private int baseRecord;
    // rowkey生成器
    private RowKeyGenerator rkGen;
    // 取样时,由取样数目及region数相除所得的数量.
    private int splitKeysBase;
    // splitkeys个数
    private int splitKeysNumber;
    // 由抽样计算出来的splitkeys结果
    private byte[][] splitKeys;
    
    /**
     * 
     * @param baseRecord  多少条数据
     * @param prepareRegions 准备多少个分区
     */
    public HashChoreWoker(int baseRecord, int prepareRegions) {
        this.baseRecord = baseRecord;
        // 实例化rowkey生成器
        rkGen = new HashRowKeyGenerator();
        splitKeysNumber = prepareRegions - 1;
        splitKeysBase = baseRecord / prepareRegions;
    }

    /**
     * 根据rowkey生成规则计算一定数量的数据的 分区的 值
     * @return
     */
    public byte[][] calcSplitKeys() {
        splitKeys = new byte[splitKeysNumber][];
        // 使用treeset保存抽样数据,已排序过
        TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
        for (int i = 0; i < baseRecord; i++) {
                    byte[] rowId = rkGen.nextId();
//        	System.out.print( Bytes.toLong( rowId) );
//            System.out.println("  ===  "+  new String(rowId) );
                    if(i%1000000==0){
                        System.out.println( i );
            }
            rows.add(rowId);
        }

        System.out.print("rowid生成结束");
        int pointer = 0;
        Iterator<byte[]> rowKeyIter = rows.iterator();
        int index = 0;
//        System.out.println( );
        while (rowKeyIter.hasNext()) {
            byte[] tempRow = rowKeyIter.next();
//            System.out.println( Bytes.toLong( tempRow ) );
            rowKeyIter.remove();
            if ((pointer != 0) && (pointer % splitKeysBase == 0)) {
                if (index < splitKeysNumber) {
                    splitKeys[index] = tempRow;
                    System.out.println(" 中间值 "+  new String(tempRow)   );
                    index++;
                }
            }
            pointer++;
        }
        rows.clear();
        rows = null;
        return splitKeys;
    }

    /**
     * 计算分区rowkey的测试程序
     *
     * @return
     */
    public byte[][] calcSplitKeysTest() {
        splitKeys = new byte[splitKeysNumber][];
        // 使用treeset保存抽样数据,已排序过
        TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
        for (int i = 1; i <= baseRecord; i++) {
            byte[] rowId = rkGen.nextIdTest();
//            System.out.print( "  ===  "+ Bytes.toLong(rowId ) );
            System.out.println("  ===  "+  new String(rowId)+"  " );
            rows.add(rowId);
        }


        int pointer = 0;
        Iterator<byte[]> rowKeyIter = rows.iterator();
        int index = 0;
//        System.out.println( );
        while (rowKeyIter.hasNext()) {
            byte[] tempRow = rowKeyIter.next();
//            System.out.println( Bytes.toLong( tempRow ) );
            rowKeyIter.remove();
            if ((pointer != 0) && (pointer % splitKeysBase == 0)) {
                if (index < splitKeysNumber) {
                    splitKeys[index] = tempRow;
                    System.out.println(" 中间值 "+ Bytes.toString(  tempRow) );
                    index++;
                }
            }
            pointer++;
        }
        rows.clear();
        rows = null;
        return splitKeys;
    }

    public static void main(String[] args) {
    	HashChoreWoker  hcw = new HashChoreWoker(5,2 );
//    	byte[][] bsk = hcw.calcSplitKeys();
//    	System.out.println( bsk );

        byte[][] bskTest = hcw.calcSplitKeysTest();
        System.out.println( bskTest );
    	
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自username2.iteye.com/blog/2106533