hbase-04 协处理器、热点、数据备份、二级索引

要点

  1. 协处理器
  2. HBase表的rowkey设计
  3. HBase表的热点
  4. HBase表的数据备份
  5. HBase二级索引

1. HBase协处理器

  • http://hbase.apache.org/book.html#cp
  • 起源:
    • Hbase 作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和、计数、排序等操作。比如,在旧版本的(<0.92)Hbase 中,统计数据表的总行数,需 要使用 Counter 方法,执行一次 MapReduce Job 才能得到。
    • 虽然 HBase 在数据存储层中集成了 MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候, 如果直接将计算过程放置在 server端,能够减少通讯开销,从而获得很好的性能提升。
    • 于是, HBase 在 0.92 之后引入了协处理器(coprocessors),实现一些激动人心的新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。

1.1 两种协处理器

  • 协处理器有两种:observer和endpoint
1.1.1 observer
  • Observer 类似于传统数据库中的触发器,当发生某些事件的时候这类协处理器会被 Server 端调用。

  • Observer Coprocessor就是一些散布在 HBase Server 端代码中的 hook 钩子, 在固定的事件发生时被调用。

    • 比如: put 操作之前有钩子函数 prePut,该函数在put操作执行前会被Region Server调用;在 put 操作之后则有 postPut 钩子函数
  • 以 HBase0.92 版本为例,它提供了三种观察者接口:

    • RegionObserver:提供客户端的数据操纵事件钩子: Get、 Put、 Delete、 Scan 等。
    • WALObserver:提供 WAL 相关操作钩子。
    • MasterObserver:提供 DDL类型的操作钩子。如创建、删除、修改数据表等。
    • 到 0.96 版本又新增一个 RegionServerObserver

在这里插入图片描述

  • 下图是以 RegionObserver 为例子讲解 Observer 这种协处理器的原理:

在这里插入图片描述

1.1.2 endpoint
  • Endpoint协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处理器执行一段 Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理
  • 最常见的用法就是进行聚集操作。
    • 如果没有协处理器,当用户需要找出一张表中的最大数据,即max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的操作。
    • 这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执 行,势必效率低下。
    • 利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase将利用底层cluster 的多个节点并发执行求最大值的操作。即在每个 Region范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给客户端。
    • 在客户端进一步将多个 Region 的最大值进一步处理而找到其中的最大值。这样整体的执行效率就会提高很多
1.1.3 总结
  • Observer允许集群在正常的客户端操作过程中可以有不同的行为表现
  • Endpoint 允许扩展集群的能力,对客户端应用开放新的运算命令
  • observer 类似于 RDBMS 中的触发器,主要在服务端工作
  • endpoint 类似于 RDBMS 中的存储过程,主要在 client 端工作
  • observer 可以实现权限管理、优先级设置、监控、 ddl 控制、 二级索引等功能
  • endpoint 可以实现 min、 max、 avg、 sum、 distinct、 group by 等功能

1.2 协处理器加载方式

  • 协处理器的加载方式有两种
    • 静态加载方式( Static Load);静态加载的协处理器称之为 System Coprocessor
    • 动态加载方式 ( Dynamic Load);动态加载的协处理器称 之为 Table Coprocessor
1.2.1 静态加载
  • 通过修改 hbase-site.xml 这个文件来实现, 如启动全局 aggregation,能过操纵所有的表数据。只需要在hbase-site.xml里面添加以下配置即可
  • 注意:修改完配置之后需要重启HBase集群
<property>
	<name>hbase.coprocessor.user.region.classes</name>
	<value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value>
</property>
  • 为所有table加载了一个 cp class,可以用” ,”分割加载多个 class,修改
1.2.2 动态加载
  • 启用表aggregation,只对特定的表生效。
  • 通过 HBase Shell 来实现。
  • disable 指定表。
hbase> disable 'mytable'
  • 添加 aggregation
hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>'|org.apache.Hadoop.hbase.coprocessor.AggregateImplementation||'
  • 重启指定表
hbase> enable 'mytable'
  • 协处理器卸载

    在这里插入图片描述

1.3 协处理器Observer实战

  • 通过协处理器Observer实现向hbase当中一张表插入数据时,通过协处理器,将数据复制一份保存到另外一张表当中去;但是只取第一张表当中的部分列数据,保存到第二张表当中去
1.3.1 创建第一张表proc1
  • 打开hbase shell
cd /kkb/install/hbase-1.2.0-cdh5.14.2/
bin/hbase shell
  • 在HBase当中创建一张表,表名user2,并只有一个列族info
hbase(main):053:0> create 'proc1','info'
1.3.2 创建第二张表proc2
  • 创建第二张表proc2,作为目标表
  • 将第一张表当中插入数据的部分列,使用协处理器,复制到proc2表当中来
hbase(main):054:0> create 'proc2','info'
1.3.3 开发HBase协处理器
  • 创建maven工程所用的repositories、dependencies、plugins跟之前的一样
    <repositories>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.6.0-mr1-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.2.0-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.2.0-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <!--    <verbal>true</verbal>-->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*/RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 开发HBase的协处理器
package com.kaikeba.hbase.cp;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;

import java.io.IOException;
import java.util.List;

public class MyProcessor extends BaseRegionObserver {
    /**
     *
     * @param e
     * @param put   插入到proc1表里面的数据,都是封装在put对象里面了
     *              插入到proc1表里面的数据都在put对象里面,就可以解析put对象,获取数据,获取到了数据之后,插入到proc2表里面去
     * @param edit
     * @param durability
     * @throws IOException
     */
    @Override
    public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
        //获取连接
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);

        //涉及到多个版本问题
        List<Cell> cells = put.get("info".getBytes(), "name".getBytes());
        Cell nameCell = cells.get(0);//获取最新的那个版本数据
        //Cell nameCell = put.get("info".getBytes(), "name".getBytes()).get(0);
        Put put1 = new Put(put.getRow());
        put1.add(nameCell);
        Table reverseuser = connection.getTable(TableName.valueOf("proc2"));
        reverseuser.put(put1);
        reverseuser.close();
        connection.close();
    }
}
1.3.4 将项目打成jar包,并上传到HDFS上面
  • 将我们的协处理器打成一个jar包,此处不需要用任何的打包插件即可
  • 然后将打好的jar包上传到linux的/kkb/install路径下
  • 再将jar包上传到HDFS
cd /kkb/install
mv original-hbaseStudy-1.0-SNAPSHOT.jar  processor.jar
hdfs dfs -mkdir -p /processor
hdfs dfs -put processor.jar /processor
1.3.5 将jar包挂载到proc1表
hbase(main):056:0> describe 'proc1'
hbase(main):055:0> alter 'proc1',METHOD => 'table_att','Coprocessor'=>'hdfs://node01:8020/processor/processor.jar|com.kkb.hbasemr.MyProcessor|1001|'
  • 再次查看’proc1’表;可以查看到我们的卸载器已经加载了
hbase(main):043:0> describe 'proc1'
1.3.6 向proc1表添加数据
package com.kaikeba.hbase.cp.test;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.Test;

public class TestObserver {
    @Test
    public void testPut() throws Exception{
        //获取连接
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "node01,node02");

        Connection connection = ConnectionFactory.createConnection(configuration);

        Table user5 = connection.getTable(TableName.valueOf("proc1"));

        Put put1 = new Put(Bytes.toBytes("hello_world"));

        put1.addColumn(Bytes.toBytes("info"),"name".getBytes(),"helloworld".getBytes());
        put1.addColumn(Bytes.toBytes("info"),"gender".getBytes(),"abc".getBytes());
        put1.addColumn(Bytes.toBytes("info"),"nationality".getBytes(),"test".getBytes());
        user5.put(put1);
        byte[] row = put1.getRow();
        System.out.println(Bytes.toString(row));
        user5.close();
    }
}
  • 注意:如果需要卸载我们的协处理器,那么进入hbase的shell命令行,执行以下命令即可
disable 'proc1'
alter 'proc1',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
enable 'proc1'

2. HBase表的rowkey设计

  • rowkey设计三原则

2.1 rowkey长度原则

  • rowkey是一个二进制码流,可以是任意字符串,最大长度64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长。
  • 建议尽可能短;但是也不能太短,否则rowkey前缀重复的概率增大
  • 设计过长会降低memstore内存的利用率和HFile存储数据的效率。

2.2 rowkey散列原则

  • 建议将rowkey的高位作为散列字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率。
  • 如果没有散列字段,首字段直接是时间信息。所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

2.3 rowkey唯一原则

  • 必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的
  • 因此,设计rowkey的时候,要充分利用这个排序的特点,可以将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块
  • 下图为电信上网详单数据,保存在HBase的一个应用场景
    在这里插入图片描述

3. HBase表的热点

3.2 什么是热点

  • 检索habse的记录首先要通过row key来定位数据行。
  • 当大量的client访问hbase集群的一个或少数几个节点,造成少数region server的读/写请求过多、负载过大,而其他region server负载却很小,就造成了“热点”现象。

3.2 热点的解决方案

3.2.1 预分区
  • 预分区的目的让表的数据可以均衡的分散在集群中,而不是默认只有一个region分布在集群的一个节点上。
3.2.3 加盐
  • 这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同
3.2.4 哈希
  • 哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。
rowkey=MD5(username).subString(0,10)+时间戳	
3.2.4 反转
  • 反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。
  • 这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。
电信公司:
移动-----------> 136xxxx9301  ----->1039xxxx631
				136xxxx1234  
				136xxxx2341 
电信
联通

user表
rowkey    name    age   sex    address
		  lisi1    21     m       beijing
		  lisi2    22     m       beijing
		  lisi3    25     m       beijing
		  lisi4    30     m       beijing
		  lisi5    40     f       shanghai
		  lisi6    50     f       tianjin
	          
需求:后期想经常按照居住地和年龄进行查询?	
rowkey= address+age+随机数
        beijing21+随机数
        beijing22+随机数
        beijing25+随机数
        beijing30+随机数
   
rowkey= address+age+随机数

4. HBase的数据备份

4.1 基于HBase提供的类对表进行备份

  • 使用HBase提供的类把HBase中某张表的数据导出到HDFS,之后再导出到测试hbase表中。

  • (1) 从hbase表导出到HDFS

    [hadoop@node01 shells]$ hbase org.apache.hadoop.hbase.mapreduce.Export myuser /hbase_data/myuser_bak
    
    
  • (2) 文件导入hbase表

    hbase shell中创建备份目标表

    create 'myuser_bak','f1','f2'
    
  • 将HDFS上的数据导入到备份目标表中

    hbase org.apache.hadoop.hbase.mapreduce.Driver import myuser_bak /hbase_data/myuser_bak/*
    
  • 补充说明

    以上都是对数据进行了全量备份,后期也可以实现表的增量数据备份,增量备份跟全量备份操作差不多,只不过要在后面加上时间戳。

    例如:
    HBase数据导出到HDFS

    hbase org.apache.hadoop.hbase.mapreduce.Export test /hbase_data/test_bak_increment 开始时间戳  结束时间戳
    

4.2 基于snapshot快照对表进行备份

  • 通过snapshot快照的方式实现HBase数据的迁移和拷贝。这种方式比较常用,效率高,也是最为推荐的数据迁移方式。

  • HBase的snapshot其实就是一组metadata信息的集合(文件列表),通过这些metadata信息的集合,就能将表的数据回滚到snapshot那个时刻的数据。

  • 首先我们要了解一下所谓的HBase的LSM类型的系统结构,我们知道在HBase中,数据是先写入到Memstore中,当Memstore中的数据达到一定条件,就会flush到HDFS中,形成HFile,后面就不允许原地修改或者删除了。

  • 如果要更新或者删除的话,只能追加写入新文件。既然数据写入以后就不会在发生原地修改或者删除,这就是snapshot做文章的地方。做snapshot的时候,只需要给快照表对应的所有文件创建好指针(元数据集合),恢复的时候只需要根据这些指针找到对应的文件进行恢复就Ok。这是原理的最简单的描述,下图是描述快照时候的简单流程:

在这里插入图片描述

4.3 快照实战

  • 1、创建表的snapshot
snapshot 'tableName', 'snapshotName'
  • 2、查看snapshot
list_snapshots  

​ 查找以test开头的snapshot

list_snapshots 'test.*'
  • 3、恢复snapshot

​ ps:这里需要对表进行disable操作,先把表置为不可用状态,然后在进行进行restore_snapshot的操作

disable 'tableName'
restore_snapshot 'snapshotName'
enable 'tableName'
  • 4、删除snapshot
delete_snapshot 'snapshotName'
  • 5、迁移 snapshot
hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
-snapshot snapshotName  \
-copy-from hdfs://src-hbase-root-dir/hbase \
-copy-to hdfs://dst-hbase-root-dir/hbase \
-mappers 1 \
-bandwidth 1024

例如:
hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
-snapshot test  \
-copy-from hdfs://node01:8020/hbase \
-copy-to hdfs://node01:8020/hbase1 \
-mappers 1 \
-bandwidth 1024

​ 注意:这种方式用于将快照表迁移到另外一个集群的时候使用,使用MR进行数据的拷贝,速度很快,使用的时候记得设置好bandwidth参数,以免由于网络打满导致的线上业务故障。

  • 6、将snapshot使用bulkload的方式导入
hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles \
hdfs://dst-hbase-root-dir/hbase/archive/datapath/tablename/filename \
tablename

例如:
创建一个新表
create 'newTest','f1','f2'
hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://node1:9000/hbase1/archive/data/default/test/6325fabb429bf45c5dcbbe672225f1fb newTest

5. HBase二级索引

在这里插入图片描述

  • HBase表后期按照rowkey查询性能是最高的。rowkey就相当于hbase表的一级索引

  • 但是在实际的工作中,我们做的查询基本上都是按照一定的条件进行查找,无法事先知道满足这些条件的rowkey是什么,正常是可以通过hbase过滤器去实现。但是效率非常低,这是由于查询的过程中需要在底层进行大量的文件扫描。

  • HBase的二级索引

  • 为了HBase的数据查询更高效、适应更多的场景,诸如使用非rowkey字段检索也能做到秒级响应,或者支持各个字段进行模糊查询和多字段组合查询等, 因此需要在HBase上面构建二级索引, 以满足现实中更复杂多样的业务需求。

    • hbase的二级索引其本质就是建立HBase表中列与行键之间的映射关系。

在这里插入图片描述

  • 构建hbase二级索引方案
    • MapReduce方案
    • Hbase Coprocessor(协处理器)方案
    • Solr+hbase方案
    • ES+hbase方案
    • Phoenix+hbase方案(参见phoenix安装部署,构建二级索引)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小技工丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值