大数据技术之HBase2

简介: 大数据技术之HBase2

3 HBase API

3.1、环境准备

新建项目后在pom.xml 中添加依赖

注意:会报错javax.el 包不存在,是一个测试用的依赖,不影响使用

<dependencies>
    <dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>2.4.11</version>
        <exclusions> 
            <exclusion>
                <groupId>org.glassfish</groupId>
                <artifactId>javax.el</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b06</version>
    </dependency>
</dependencies>

3.2、创建连接

根据官方API 介绍,HBase 的客户端连接由ConnectionFactory 类来创建,用户使用完成之后需要手动关闭连接。同时连接是一个重量级的,推荐一个进程使用一个连接,对HBase 的命令通过连接中的两个属性Admin 和Table 来实现


3.2.1、单线程创建连接

package com.atguigu.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection; 
import  org.apache.hadoop.hbase.client.Connection; 
import org.apache.hadoop.hbase.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
public class HBaseConnect {
 public static void main(String[] args) throws IOException {
     // 1. 创建配置对象
     Configuration conf = new Configuration();
     // 2. 添加配置参数
    conf.set("hbase.zookeeper.quorum","hadoop102,hadoop103,hadoop104");
     // 3. 创建 hbase 的连接
     // 默认使用同步连接
     Connection connection = ConnectionFactory.createConnection(conf);
     // 可以使用异步连接
     // 主要影响后续的 DML 操作
     CompletableFuture<AsyncConnection> asyncConnection = ConnectionFactory.createAsyncConnection(conf);
     // 4. 使用连接
     System.out.println(connection);
     // 5. 关闭连接
     connection.close();
     }
}


3.2.2、多线程创建连接

使用类单例模式,确保使用一个连接,可以同时用于多个线程。

 package com.atguigu;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
public class HBaseConnect {
     // 设置静态属性 hbase 连接
     public static Connection connection = null;
     static {
         // 创建 hbase 的连接
         try {
             // 使用配置文件的方法
             connection = ConnectionFactory.createConnection();
         } catch (IOException e) {
             System.out.println("连接获取失败");
             e.printStackTrace();
         }
     }
     /**
     * 连接关闭方法,用于进程关闭时调用
     * @throws IOException
     */
     public static void closeConnection() throws IOException {
         if (connection != null) {
             connection.close();
         }
     }
}

在 resources 文件夹中创建配置文件 hbase-site.xml,添加以下内容

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
     <property>
         <name>hbase.zookeeper.quorum</name>
         <value>hadoop102,hadoop103,hadoop104</value>
     </property>
</configuration>


3.3 、DDL

创建 HBaseDDL 类,添加静态方法即可作为工具类

public class HBaseDDL {
     // 添加静态属性 connection 指向单例连接
     public static Connection connection = HBaseConnect.connection;
}

3.3.1 、创建命名空间

/**
 * 创建命名空间
 * @param namespace 命名空间名称
 */
 public static void createNamespace(String namespace) throws IOException {
     // 1. 获取 admin
     // 此处的异常先不要抛出 等待方法写完 再统一进行处理
     // admin 的连接是轻量级的 不是线程安全的 不推荐池化或者缓存这个连接
     Admin admin = connection.getAdmin();
     // 2. 调用方法创建命名空间
     // 代码相对 shell 更加底层 所以 shell 能够实现的功能 代码一定能实现
     // 所以需要填写完整的命名空间描述
     // 2.1 创建命令空间描述建造者 => 设计师
     NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);
     // 2.2 给命令空间添加需求
     builder.addConfiguration("user","atguigu");
     // 2.3 使用 builder 构造出对应的添加完参数的对象 完成创建
     // 创建命名空间出现的问题 都属于本方法自身的问题 不应该抛出
     try {
         admin.createNamespace(builder.build());
     } catch (IOException e) {
         System.out.println("命令空间已经存在");
         e.printStackTrace();
     }
     // 3. 关闭 admin
     admin.close();
 }


骚戴理解: admin.createNamespace(builder.build());的异常是同try-catch处理的,为什么不直接抛出去呢?这是因为直接抛出去的话,假如出现了异常,那么异常代码后面的代码就都不会执行了,如果是try-catch捕获处理了异常,那么异常代码后的代码都会继续正常的执行


3.3.2 、判断表格是否存在

/**
 * 判断表格是否存在
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @return ture 表示存在
 */
 public static boolean isTableExists(String namespace,String tableName) throws IOException {
     // 1. 获取 admin
     Admin admin = connection.getAdmin();
     // 2. 使用方法判断表格是否存在
     boolean b = false;
     try {
         b = admin.tableExists(TableName.valueOf(namespace, tableName));
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 3. 关闭 admin
     admin.close();
     // 3. 返回结果
     return b;
     // 后面的代码不能生效
 }

骚戴理解:这里的admin.close();要写在 return b;的前面,因为return表示方法结束了,就直接退出方法了,那写在后面的话是不会执行的!!获取TableName对象的时候不能直接通过new来获取,因为这类的构造方法是私有的,所以通过静态方法TableName.valueOf(namespace, tableName)来获取TableName对象

3.3.3 、创建表

/**
 * 创建表格
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @param columnFamilies 列族名称 可以有多个
 */
 public static void createTable(String namespace , String tableName , String...columnFamilies) throws IOException {
     // 判断是否有至少一个列族
     if (columnFamilies.length == 0){
         System.out.println("创建表格至少有一个列族");
         return;
     }
     // 判断表格是否存在
     if (isTableExists(namespace,tableName)){
         System.out.println("表格已经存在");
         return;
     }
     // 1.获取 admin
     Admin admin = connection.getAdmin();
     // 2. 调用方法创建表格
     // 2.1 创建表格描述的建造者
     TableDescriptorBuilder tableDescriptorBuilder = 
     TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace, tableName));
     // 2.2 添加参数
     for (String columnFamily : columnFamilies) {
     // 2.3 创建列族描述的建造者
         ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));
         // 2.4 对应当前的列族添加参数
         // 添加版本参数
         columnFamilyDescriptorBuilder.setMaxVersions(5);
         // 2.5 创建添加完参数的列族描述
        tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build());
     }
     // 2.6 创建对应的表格描述
     try {
         admin.createTable(tableDescriptorBuilder.build());
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 3. 关闭 admin
     admin.close();
 }

骚戴理解: String...columnFamilies是可变参数,可以理解为其实就是一个数组,可以传入多个值

3.3.4、 修改表

/**
 * 修改表格中一个列族的版本
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @param columnFamily 列族名称
 * @param version 版本
 */
 public static void modifyTable(String namespace ,String tableName,String columnFamily,int version) throws IOException {
     // 判断表格是否存在
     if (!isTableExists(namespace,tableName)){
         System.out.println("表格不存在无法修改");
         return;
     }
     // 1. 获取 admin
     Admin admin = connection.getAdmin();
     try {
         // 2. 调用方法修改表格
         // 2.0 获取之前的表格描述
         TableDescriptor descriptor = 
        admin.getDescriptor(TableName.valueOf(namespace, tableName));
         // 2.1 创建一个表格描述建造者
         // 如果使用填写 tableName 的方法 相当于创建了一个新的表格描述建造
        者 没有之前的信息
         // 如果想要修改之前的信息 必须调用方法填写一个旧的表格描述
         TableDescriptorBuilder tableDescriptorBuilder = 
        TableDescriptorBuilder.newBuilder(descriptor);
         // 2.2 对应建造者进行表格数据的修改
         ColumnFamilyDescriptor columnFamily1 = 
        descriptor.getColumnFamily(Bytes.toBytes(columnFamily));
         // 创建列族描述建造者
         // 需要填写旧的列族描述
         ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = 
        ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);
         // 修改对应的版本
         columnFamilyDescriptorBuilder.setMaxVersions(version);
         // 此处修改的时候 如果填写的新创建 那么别的参数会初始化
        tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorB
        uilder.build());
        admin.modifyTable(tableDescriptorBuilder.build());
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 3. 关闭 admin
     admin.close();
 }

骚戴理解:如果想要修改之前的信息 必须调用方法填写一个旧的表格描述,也就是不能像上面创建表描述一样直接新建一个TableDescriptorBuilder表格描述对象,而是通过下面的语句来获取旧的表描述

  TableDescriptor descriptor =admin.getDescriptor(TableName.valueOf(namespace,tableName));

同样,修改列值也要用原来旧的表描述的列族,通过下面的语句来获取旧的列族描述

 ColumnFamilyDescriptor columnFamily1 = descriptor.getColumnFamily(Bytes.toBytes(columnFamily));

3.3.5 、删除表

/**
 * 删除表格
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @return true 表示删除成功
 */
 public static boolean deleteTable(String namespace ,String tableName) throws IOException {
     // 1. 判断表格是否存在
     if (!isTableExists(namespace,tableName)){
         System.out.println("表格不存在 无法删除");
         return false;
     }
     // 2. 获取 admin
     Admin admin = connection.getAdmin();
     // 3. 调用相关的方法删除表格
     try {
         // HBase 删除表格之前 一定要先标记表格为不可以
         TableName tableName1 = TableName.valueOf(namespace, tableName);
         admin.disableTable(tableName1);
         admin.deleteTable(tableName1);
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 4. 关闭 admin
     admin.close();
     return true;
 }


骚戴理解: HBase 删除表格之前 一定要先标记表格为不可以,通过admin.disableTable(tableName1);语句来设置表格为不可用

3.4 、DML

创建类 HBaseDML

public class HBaseDML {
 // 添加静态属性 connection 指向单例连接
 public static Connection connection = HBaseConnect.connection;
}


3.4.1、 插入数据

/**
 * 插入数据
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @param rowKey 主键
 * @param columnFamily 列族名称
 * @param columnName 列名
 * @param value 值
 */
 public static void putCell(String namespace,String tableName,String rowKey, String columnFamily,String columnName,String value) throws IOException {
     // 1. 获取 table
     Table table = connection.getTable(TableName.valueOf(namespace, tableName));
     // 2. 调用相关方法插入数据
     // 2.1 创建 put 对象
     Put put = new Put(Bytes.toBytes(rowKey));
     // 2.2. 给 put 对象添加数据
    put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName),Bytes.toBytes(value));
     // 2.3 将对象写入对应的方法
     try {
         table.put(put);
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 3. 关闭 table
     table.close();
 }

3.4.2 、读取数据(读取对应的一行中的某一列)

/**
 * 读取数据 读取对应的一行中的某一列
 *
 * @param namespace 命名空间名称
 * @param tableName 表格名称
 * @param rowKey 主键
 * @param columnFamily 列族名称
 * @param columnName 列名
 */
 public static void getCells(String namespace, String tableName, String rowKey, String columnFamily, String columnName) throws IOException {
     // 1. 获取 table
     Table table = connection.getTable(TableName.valueOf(namespace, tableName));
     // 2. 创建 get 对象
     Get get = new Get(Bytes.toBytes(rowKey));
     // 如果直接调用 get 方法读取数据 此时读一整行数据
     // 如果想读取某一列的数据 需要添加对应的参数
     get.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnName));
     // 设置读取数据的版本
     get.readAllVersions();
     try {
     // 读取数据 得到 result 对象
     Result result = table.get(get);
     // 处理数据
     Cell[] cells = result.rawCells();
     // 测试方法: 直接把读取的数据打印到控制台
     // 如果是实际开发 需要再额外写方法 对应处理数据
     for (Cell cell : cells) {
         // cell 存储数据比较底层
         String value = new String(CellUtil.cloneValue(cell));
         System.out.println(value);
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 关闭 table
     table.close();
 }


骚戴理解:String value = new String(CellUtil.cloneValue(cell));这句是为了防止乱码,通过这句代码把底层的cell转成字符串来方便打印出来

3.4.3 、扫描数据

/**
 * 扫描数据
 *
 * @param namespace 命名空间
 * @param tableName 表格名称
 * @param startRow 开始的 row 包含的
 * @param stopRow 结束的 row 不包含
 */
 public static void scanRows(String namespace, String tableName, String startRow, String stopRow) throws IOException {
     // 1. 获取 table
     Table table = connection.getTable(TableName.valueOf(namespace, tableName));
     // 2. 创建 scan 对象
     Scan scan = new Scan();
     // 如果此时直接调用 会直接扫描整张表
     // 添加参数 来控制扫描的数据
     // 默认包含
     scan.withStartRow(Bytes.toBytes(startRow));
     // 默认不包含
     scan.withStopRow(Bytes.toBytes(stopRow));
     try {
     // 读取多行数据 获得 scanner
     ResultScanner scanner = table.getScanner(scan);
     // result 来记录一行数据 cell 数组
     // ResultScanner 来记录多行数据 result 的数组
     for (Result result : scanner) {
        Cell[] cells = result.rawCells();
     for (Cell cell : cells) {
         System.out.print (new 
            String(CellUtil.cloneRow(cell)) + "-" + new 
            String(CellUtil.cloneFamily(cell)) + "-" + new 
            String(CellUtil.cloneQualifier(cell)) + "-" + new 
            String(CellUtil.cloneValue(cell)) + "\t");
             }
         System.out.println();
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
     // 3. 关闭 table
     table.close();
 }

骚戴理解:result 来记录一行数据 cell 数组, ResultScanner 来记录多行数据 result 的数组这两句话说的非常精准,这里要理解什么是cell才能理解那个双重for循环


Cell由{rowkey, column Family:column Qualifier, timestamp} 唯一确定的单元。cell 中的数据全部是字节码形式存贮。下面就是一个cell,对照下面的图片和代码比较一下

   // result 来记录一行数据 cell 数组
     // ResultScanner 来记录多行数据 result 的数组
     for (Result result : scanner) {
        Cell[] cells = result.rawCells();
     for (Cell cell : cells) {
         System.out.print (new 
            String(CellUtil.cloneRow(cell)) + "-" + new 
            String(CellUtil.cloneFamily(cell)) + "-" + new 
            String(CellUtil.cloneQualifier(cell)) + "-" + new 
            String(CellUtil.cloneValue(cell)) + "\t");
             }
         System.out.println();
         }

对比就可以知道这个Cell[]数组其实里面就是每一行的每一个列值,而不是所有的行!

看下面的打印结果就能知道了

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
目录
相关文章
|
4天前
|
存储 分布式计算 大数据
HBase分布式数据库关键技术与实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入剖析了HBase的核心技术,包括数据模型、分布式架构、访问模式和一致性保证,并探讨了其实战应用,如大规模数据存储、实时数据分析及与Hadoop、Spark集成。同时,分享了面试经验,对比了HBase与其他数据库的差异,提出了应对挑战的解决方案,展望了HBase的未来趋势。通过Java API代码示例,帮助读者巩固理解。全面了解和掌握HBase,能为面试和实际工作中的大数据处理提供坚实基础。
55 3
|
4天前
|
Cloud Native 数据处理 云计算
探索云原生技术在大数据分析中的应用
随着云计算技术的不断发展,云原生架构作为一种全新的软件开发和部署模式,正逐渐引起企业的广泛关注。本文将探讨云原生技术在大数据分析领域的应用,介绍其优势与挑战,并探讨如何利用云原生技术提升大数据分析的效率和可靠性。
|
4天前
|
分布式计算 Hadoop 大数据
大数据技术与Python:结合Spark和Hadoop进行分布式计算
【4月更文挑战第12天】本文介绍了大数据技术及其4V特性,阐述了Hadoop和Spark在大数据处理中的作用。Hadoop提供分布式文件系统和MapReduce,Spark则为内存计算提供快速处理能力。通过Python结合Spark和Hadoop,可在分布式环境中进行数据处理和分析。文章详细讲解了如何配置Python环境、安装Spark和Hadoop,以及使用Python编写和提交代码到集群进行计算。掌握这些技能有助于应对大数据挑战。
|
4天前
|
存储 数据采集 数据可视化
大数据处理技术
【4月更文挑战第10天】大数据处理涵盖采集、预处理、存储、分析挖掘、展现和应用等关键步骤。采集涉及多种类型数据,预处理确保数据质量,存储管理关注规模、速度和安全,分析挖掘利用机器学习发现价值,展现和应用则通过可视化和检索实现数据价值。云计算和AI强化了大数据处理能力,整体目标是提取数据中的价值,驱动企业和社会进步。
35 4
大数据处理技术
|
4天前
|
机器学习/深度学习 运维 算法
大数据基础工程技术团队4篇论文入选ICLR,ICDE,WWW
近日,由阿里云计算平台大数据基础工程技术团队主导的四篇时间序列相关论文分别被国际顶会ICLR2024、ICDE2024和WWW2024接收。
|
4天前
|
存储 机器学习/深度学习 数据采集
大数据处理与分析实战:技术深度剖析与案例分享
【5月更文挑战第2天】本文探讨了大数据处理与分析的关键环节,包括数据采集、预处理、存储、分析和可视化,并介绍了Hadoop、Spark和机器学习等核心技术。通过电商推荐系统和智慧城市交通管理的实战案例,展示了大数据在提高用户体验和解决实际问题上的效能。随着技术进步,大数据处理与分析将在更多领域发挥作用,推动社会进步。
|
4天前
|
存储 数据可视化 大数据
大数据技术框架
【4月更文挑战第20天】大数据一般需要经过6个主要环节,包括数据收集、数据存储、资源管理与服务协调、计算引擎、数据分析和数据可视化。
|
5天前
|
存储 大数据 数据管理
大数据技术是如何发展的?
大数据虽已发展多年,但仍面临将恰当信息在正确时间传递给合适人员的挑战。尽管技术进步,大部分企业员工仍难以获取所需信息。数据仓库、数据湖和数据结构等存储系统涌现,但集中数据并不实际,数据去中心化趋势明显。数据结构允许异构数据并促进治理,同时,云计算影响大数据战略,提供灵活实验空间。数据治理和隐私规则的遵守至关重要,流程成熟度聚焦于数据质量和共享。目前大数据正处于“幻灭低谷”,成功的关键在于数据治理和处理流程的改进。
|
5天前
|
NoSQL 大数据 数据挖掘
现代数据库技术与大数据应用
随着信息时代的到来,数据量呈指数级增长,对数据库技术提出了前所未有的挑战。本文将介绍现代数据库技术在处理大数据应用中的重要性,并探讨了一些流行的数据库解决方案及其在实际应用中的优势。
|
5天前
|
机器学习/深度学习 人工智能 数据可视化
基于Python的数据可视化技术在大数据分析中的应用
传统的大数据分析往往注重数据处理和计算,然而数据可视化作为一种重要的技术手段,在大数据分析中扮演着至关重要的角色。本文将介绍如何利用Python语言中丰富的数据可视化工具,结合大数据分析,实现更直观、高效的数据展示与分析。