Hbase写入原理-常用操作-过滤器

hbase是基于大表的数据库
=====================================
    随机访问和实时读写


hbase和hive的区别:
    hbase:低延迟实时性,不支持分析
    hive:高延迟,分析工具

awk '{print $1}'    //默认以'\t'分割,截串取第一个成员


hbase原理:
========================
    hdfs://mycluster/user/hbase/data    //hbase数据
    
    namespace    //文件夹
    Htable        //文件夹
    Hregion        //区域,文件夹
    CF        //列族, 文件夹
    数据        //文件形式存在(row,cf,col,value)


hbase在写入数据时,先写入到memstore中,到达一定的值或重启的时候会实例化为文件



scan 'hbase:meta'
    ns1:t1,,1522573626932.02aab65df5b31 column=info:regioninfo, timestamp=1522630951985, value={ENCODED => 02aab65df5b31c1145a8b799ced42c92, NAM
    c1145a8b799ced42c92.                E => 'ns1:t1,,1522573626932.02aab65df5b31c1145a8b799ced42c92.', STARTKEY => '', ENDKEY => ''}
    ns1:t1,,1522573626932.02aab65df5b31 column=info:seqnumDuringOpen, timestamp=1522630951985, value=\x00\x00\x00\x00\x00\x00\x00\x15
    c1145a8b799ced42c92.
    ns1:t1,,1522573626932.02aab65df5b31 column=info:server, timestamp=1522630951985, value=s102:16020
    c1145a8b799ced42c92.
    ns1:t1,,1522573626932.02aab65df5b31 column=info:serverstartcode, timestamp=1522630951985, value=1522629569862
    c1145a8b799ced42c92.


hbase:meta表存放的是所有表的元数据,即表(区域)和regionserver的映射关系

    问题1:hbase:meta表是谁负责管辖?        
        //每个表(包括元数据表),都分别由一个regionserver负责的

    问题2:元数据中的元数据,hbase:meta的元数据存放位置?    
        //hbase:meta表的数据信息,由zk负责管辖

    普通表:    表体由regionserver负责管理,元数据是由hbase:meta表负责管理
    hbase:meta表:  表体由regionserver负责管理,元数据是由zk负责管辖


    写(put)流程:    1、客户端先联系zk,找到hbase:meta表的regionserver,在此请求数据
            2、通过hbase:meta的数据,找到表(区域)的映射关系,通过regionserver定位到指定表(区域)
            3、查询信息一旦被触发,会被缓存,以便下次使用
            4、用户向区域服务器(RegionServer)发起put请求时,会将请求交给对应的区域(region)实例来处理。    
            5、决定数据是否需要写到由HLog实现的预写日志(WAL)中。
            6、一旦数据被写入到WAL中,数据会被放到MemStore中
            7、memstore如果写满,则会被刷写(flush)到磁盘中,生成新的hfile

区域:region管理
======================================
    是regionserver的基本管理单位

    rowKey是以字节数组进行排序

    表切割:
        split 'ns1:t1','row500'

    区域切割:
        split 'ns1:t1,row500,1522634115455.b88d6163f7e2eced2f2297c7fe938b3b.','row750'

    
    创建表,进行预切割:
        create 'ns1:t2', 'f1', SPLITS => [ '20', '30', '40']
        
    移动区域:
        move '43e14524a984a4bf6a813a4f2a8ac8eb' , 's103,16020,1522634665677'


    表切割之后,之前表所在的区域会被切割成两个新的区域,原区域处于离线状态,再次启动hbase时,原区域相关信息会被清空
        

    区域合并
         merge_region '28d725266b30bcef636e994f08c9e8d9','976bc7a3b8769184c1a0a5aca1339d99'
        

紧凑compact:
====================================
    解决flush产生的小文件(hfile),将其进行合并

    compact 'ns1:t1'            //合并小文件,重启后生效
    compact_rs 's103,16020,1522634665677'    //合并regionserver中所有region
    major_compact                //合并小文件,即刻生效

    

        
regionname        //区域名,eg:ns1:t1,row500,1522634115455.b88d6163f7e2eced2f2297c7fe938b3b.
encoded_regionname    //编码区域名,eg:b88d6163f7e2eced2f2297c7fe938b3b    注意不加'.'
server_name        //regionserver名字, eg:s102,16020,1522634665523


hbase数据写入流程,代码分析
==========================================================
在数据写入的时候,hbase会初始化一个2M的mutator缓冲区
          通过Arrays.asList(m)将Mutation对象,即数据转换成List
          writeAsyncBuffer证明,数据写入是异步写入
          每次写入数据都会在内部进行自动清理
          写入时将传来的Mutation对象,即数据放在LinkedList中

          put命令时:1、传入put对象
                在传每个数据时候,都会进行autoflush自动清理,且需要一次rpc通信

                 2、传入put集合
                将集合传入并且处理之后,进行一次flush,需要一次rpc通信

                10000        关闭自动flush    关闭写前日志    关闭flush+WAL
        ----------------------------------------------------------------------
        put        38,532ms    3,227ms        10,769ms    2,959ms

        putBatch    3,507ms

        
        在put中设置关闭自动flush
            HTable.setAutoFlush(false,false);

        
        在put中关闭WAL写入
             put.setDurability(Durability.SKIP_WAL);


WAL
=============================
    Write ahead log
    适用于容灾,当机器发生断电,memstore数据很可能会丢失

    WAL:数据写入到WAL则证明写入成功,存储对数据的修改,类似于hadoop的edits文件
         如果数据库崩溃,可以有效地回放日志

*****    强烈建议用户不要关闭WAL
    实现类:org.apache.hadoop.hbase.regionserver.wal.FSHLog
    
    WAL在0.x版本中使用的是SeqFile
    新版中不是

memstore:
================================
    1、刷写到磁盘,阈值是5M
      <property>
        <name>hbase.hregion.preclose.flush.size</name>
        <value>5242880</value>
      </property>
    
    2、关闭regionserver会使memstore强制刷写到磁盘




hbase基本命令:
============================
    create_namespace 'ns1'   //create_namespace 'ns1'
    drop_namespace 'ns1'        //drop_namespace 'ns1'
    create 'ns1:t1','f1','f2'    //create 'ns1:t1','f1','f2'
    put 'ns1:t1','row1','f1:name','tom'      //put 'ns1:t1','row1','f1:name','tom'
    scan 'ns1:t1'                            //scan 'ns1:t1'
    alter 'ns1:t5', 'f3'        //添加列族 //alter 'ns1:t5' , 'f3'



hbase文件:
===========================
    根级文件:/user/hbase
        WALs        //预写日志,和hadoop中的edits文件类似,作用是容灾
                //通过以下配置进行周期性滚动
                 <property>
                    <name>hbase.regionserver.logroll.period</name>
                    <value>3600000</value>
                    <description>毫秒为单位,默认一小时</description>
                 </property>


        oldWALs        //旧的预写日志,当时间超过1小时,WAL会被回滚到此处
                //十分钟后此文件会被清除,配置文件如下
                 <property>
                    <name>hbase.master.logcleaner.ttl</name>
                    <value>600000</value>
                    <description>毫秒为单位,默认十分钟</description>
                  </property>

        hbase.id    //hbase唯一id
        hbase.version    //hbase集群版本信息

        corrupt        //损坏日志文件

    表级文件:/user/hbase/data/ns1/t1

        .tabledesc/.tableinfo.0000000001    //串行化后的表描述符desc 'ns1:t1'

    
    region级文件:
        .regioninfo        //region的描述,和tableinfo类似

        recovered.edits        //WAL日志恢复文件
        
    
当region足够大的时候,会自动切分成两个region,将原文件一分为二  region默认大小是10G
==================================================
      <property>
        <name>hbase.hregion.max.filesize</name>
        <value>10737418240</value>
        <description>
        Maximum HStoreFile size. If any one of a column families' HStoreFiles has
        grown to exceed this value, the hosting HRegion is split in two.</description>
      </property>

     注意:当所有region同时达到阈值时候,会产生切割风暴,严重消耗资源

     1、设置预切割:
        create 'ns1:t2', 'f1', SPLITS => [ '20', '30', '40']

     2、设置一个非常大的值,然后手动切割


HFile:
=============================
    是hbase的文件格式,以k/v形式存储,k/v均是字节数组

    HFile包括以下内容:
        读取或写入压缩块的存储空间。 
        每个块所指定的I/O操作的压缩编解码器
        临时的key存储
        临时的value存储
        hfile索引,存在于内存,占用空间约为(56+AvgKeySize)*NumBlocks.
        性能优化建议
    ****    最小块大小,推荐在 8KB to 1MB之间
            顺序读写推荐大块,但不便于随机访问(因为需要解压更多的数据)
            小块便于随机读写,但是需要占用更多内存,但是创建起来更慢(因为块多,每次压缩都需要flush操作)
            由于压缩缓存,最小块大小应该在20KB-30KB.

    查看hfile:
    ====================
    hbase hfile -a -b -p -v -h -f hdfs://mycluster/user/hbase/data/ns1/t1/77c55f893ecec3bbb2dfd02e3737c0c2/f1/5406345aabc640309be64abd21a2a500
        key:row1/f1:age/1522634241444/Put/vlen=1/seqid=22
        value:1


Cell:
=============================
 * 1) row        
 * 2) column family
 * 3) column qualifier
 * 4) timestamp
 * 5) type
 * 6) MVCC version    //multiple version concurrency contorl    多版本并发控制
 * 7) value


scan操作:
=======================
    限定列扫描:
        HTable.getScanner(Bytes.toBytes("f1"),Bytes.toBytes("name"));   //HTable.getScanner(Bytes.toBytes("f1"),Bytes.toBytes("name"));

    指定列族扫描:
        HTable.getScanner(Bytes.toBytes("f1"));  //Htable.getScanner(Bytes.toBytes("f1"));
    
    全表扫描
        HTable.getScanner(Scanner)   //Htable.getScanner(Scanner)

    限定row范围进行扫描:
        new Scan(Byte[] startKey,Byte[] stopKey)   //new Scan(Byte[] startKey,Byte[] stopKey)

    
    catch和batch
        每次it.next()的时候,都会调用一次rpc,效率很差
        避免此问题,引入cache的概念

        catch:将指定数量行数进行缓存,到达阈值,通过一个RPC共同发给客户端

          <property>
            <name>hbase.client.scanner.caching</name>
            <value>2147483647</value>
            <description>每次查询缓存的数量</description>
          </property>

    
        cache    10        1000        10000        1
        -------------------------------------------------------------
        time    6,423ms        5,434ms        5,831ms        14,339ms

    
        batch: 缓存指定的列数,到达阈值,通过一个RPC共同发给客户端

        
        RPC次数:    row x col / min(cache,rowNo) / min(batch, colNo) +1
        result次数:    row x col / min(batch, colNo)            //只和batch有关


hbase过滤器:
===================================================
    相当于sql的where子句
    使用谓词下推的原理,通过server端过滤,返回给client数据

    hbase过滤器中的比较方法:
        CompareFilter.CompareOp.EQUAL    ====> '='

    使用过滤器:
        1、初始化过滤器        //new RowFilter
        2、初始化参数:比较方法    //CompareFilter.CompareOp.EQUAL
                   比较器    //
        
    过滤器:
        RowFilter            //行过滤器    //RowFilter
        FamilyFilter            //列族过滤器   //FamilyFilter
        QualifierFilter            //列过滤器   //QualifierFilter
        ValueFilter            //值过滤器  //ValueFileter
        SingleColumnValueFilter        //单列值过滤器,通过搜索制定列族和制定col的值,返回整行数据    //SingleColumnValueFilter
                        //相当于select * from xxx where id=1;
        TimeStampFilter            //时间戳过滤器   //TimeStampFilter


        
            
    
    比较器:
        BinaryComparator        //二进制比较器        //BianryComparator
        RegexStringComparator        //正则比较器, 比较时最好使用EQUAL   //RegexStringComparator
        SubstringComparator        //子串比较器, 比较时最好使用EQUAL    //SubstringComparator

        
        
        /**
     * 组合过滤器
     * @throws Exception
     */
    @Test
    public void testCombineFilter() throws Exception {
        long start = System.currentTimeMillis();
        //初始化配置信息
        //Configuration conf = new Configuration();
        Configuration conf = HBaseConfiguration.create();
        //入口点,创建连接
        Connection conn = ConnectionFactory.createConnection(conf);

        //通过getTable方法获取表的实例
        HTable table = (HTable) conn.getTable(TableName.valueOf("ns1:t5"));  
        //HTable table = (HTable) conn.getTable(TableName.valueOf("ns1:t5"));
        
        //Scan scan = new Scan();
        Scan scan = new Scan();


        //行过滤器,使用正则过滤器,过滤出111结尾的rowKey
        RowFilter filter1 = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator(".*111"));
        //RowFilter filter = new RowFilter(CompareFilter.CompareOpEQUAL,new RegexStringComparator(".*111"));

        //列族过滤器,使用二进制过滤器,过滤出f2列族的数据
        FamilyFilter filter2 = new FamilyFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("f2")));
        //FamilyFilter filter2 = new FamilyFilter(ComparaFilter.CompareOp.EQUAL,new BinaryComparator(Bytes.toBytes("f2")));
        
        
        FilterList filterList = new FilterList();   //FilterList filterList = new FilterList();
        filterList.addFilter(filter1);   //filterList.addFilter(filter1);

        filterList.addFilter(filter2);   //filterList.addFilter(filter2);

        scan.setFilter(filterList);  //scan.setFilter(filterList);

        //通过扫描器得到结果集
        ResultScanner rs = table.getScanner(scan);         ResultScanner rs = table.getScanner(scan);
        //得到迭代器
        Iterator<Result> it = rs.iterator();   rs.iterator();  

        TestFilter.printVal(it);   TestFilter.printVal(it);

        table.close(); //table.close();
        System.out.println(System.currentTimeMillis() - start);
    }
    
    
    public static void printVal(Iterator<Result> it){
        while (it.hasNext()){
            Result next = it.next();
            List<Cell> cells = next.listCells();   //next.listCells();
            for (Cell cell : cells) {
                String val = Bytes.toString(CellUtil.cloneValue(cell));    //Bytes.toString(CellUtil.cloneValue(cell));
                String clo = Bytes.toString(CellUtil.cloneQualifier(cell));  //Bytes.toString(CellUtil.cloneQualifier(cell))
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));     //Bytes.toString(CellUtil.cloneFamily(cell));
                String row = Bytes.toString(CellUtil.cloneRow(cell));    //Bytes.toString(CellUtil.cloneRow(cell)); 

                System.out.println(row+"/"+cf+"/"+clo+"/"+val);

            }


        }
    }

猜你喜欢

转载自www.cnblogs.com/zyde/p/9015523.html
今日推荐