@

底层原理

Master架构

  • Meta 表格介绍:全称 hbase:meta,只是在 list 命令中被过滤掉了,本质上和 HBase 的其他表格一样,不要去改这个表。
  • RowKey:([table],[region start key],[region id]) 即 表名,region 起始位置和 regionID。
  • 列:
    • info:regioninfo 为 region 信息,存储一个 HRegionInfo 对象。
    • info:server 当前 region 所处的 RegionServer 信息,包含端口号。
    • info:serverstartcode 当前 region 被分到 RegionServer 的起始时间。
    • 如果一个表处于切分的过程中,即 region 切分,还会多出两列 info:splitA 和 info:splitB,存储值也是 HRegionInfo 对象,拆分结束后,删除这两列。
  • 注意:在客户端对元数据进行操作的时候才会连接 master,如果对数据进行读写,直接连接zookeeper 读取目录/hbase/meta-region-server 节点信息,会记录 meta 表格的位置。直接读取即可,不需要访问 master,这样可以减轻 master 的压力,相当于 master 专注 meta 表的写操作,客户端可直接读取 meta 表。
  • 在 HBase 的 2.3 版本更新了一种新模式:Master Registry。客户端可以访问 master 来读取meta 表信息。加大了 master 的压力,减轻了 zookeeper 的压力。
  • HMaster通常部署在NameNode上,HMaster中主要有负载均衡器,元数据表管理器,预写日志管理器(MasterProcWAL)。

RegionServer架构

  • MemStore:写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile,写入到对应的文件夹 store 中。
  • WAL:由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
  • BlockCache:读缓存,每次查询出的数据会缓存在 BlockCache 中,方便下次查询。

Region/Store/StoreFile/Hfile之间的关系

  • Region

    • table在行的方向上分隔为多个Region。Region是HBase中分布式存储和负载均衡的最小单元,即不同的region可以分别在不同的Region Server上,但同一个Region是不会拆分到多个server上。
    • Region按大小分隔,表中每一行只能属于一个region。随着数据不断插入表,region不断增大,当region的某个列族达到一个阈值(默认256M)时就会分成两个新的region。
  • Store
    • 每一个region有一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个ColumnFamily建一个store(即有几个ColumnFamily,也就有几个Store)。一个Store由一个memStore和0或多个StoreFile组成。
    • HBase以store的大小来判断是否需要切分region。
  • MemStore
    • memStore 是放在内存里的。保存修改的数据即keyValues。当memStore的大小达到一个阀值(默认64MB)时,memStore会被flush到文件,即生成一个快照。目前hbase 会有一个线程来负责memStore的flush操作。
  • StoreFile
    • memStore内存中的数据写到文件后就是StoreFile(即memstore的每次flush操作都会生成一个新的StoreFile),StoreFile底层是以HFile的格式保存。
  • HFile
    • HFile是HBase中KeyValue数据的存储格式,是hadoop的二进制格式文件。一个StoreFile对应着一个HFile。而HFile是存储在HDFS之上的。HFile文件格式是基于Google Bigtable中的SSTable,如下图所示:

首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。Trailer中又指针指向其他数据块的起始点,FileInfo记录了文件的一些meta信息。

写流程

  • 首先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server;
  • 访问对应的 Region Server,获取 hbase:meta 表,将其缓存到连接中,作为连接属性 MetaCache,由于 Meta 表格具有一定的数据量,导致了创建连接比较慢; 之后使用创建的连接获取 Table,这是一个轻量级的连接,只有在第一次创建的时候会检查表格是否存在访问 RegionServer,之后在获取 Table 时不会访问 RegionServer;
  • 调用Table的put方法写入数据,此时还需要解析RowKey,对照缓存的MetaCache,查看具体写入的位置有哪个 RegionServer;
  • 将数据顺序写入(追加)到 WAL,此处写入是直接落盘的,并设置专门的线程控制 WAL 预写日志的滚动(类似 Flume);
  • 根据写入命令的 RowKey 和 ColumnFamily 查看具体写入到哪个 MemStory,并且在 MemStory 中排序;
  • 向客户端发送 ack;
  • 等达到 MemStore 的刷写时机后,将数据刷写到对应的 story 中。

写缓存刷写

MemStore Flush也即是写缓存刷写,MemStore 刷写由多个线程控制,条件互相独立:主要的刷写规则是控制刷写文件的大小,在每一个刷写线程中都会进行监控

  • 当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size(默认值 128M),其所在 region 的所有 memstore 都会刷写。当 memstore 的大小达到了hbase.hregion.memstore.flush.size(默认值 128M)* hbase.hregion.memstore.block.multiplier(默认值 4)时,会刷写同时阻止继续往该 memstore 写数据(由于线程监控是周期性的,所有有可能面对数据洪峰,尽管可能性比较小)
  • 由 HRegionServer 中的属性 MemStoreFlusher 内部线程 FlushHandler 控制。标准为LOWER_MARK(低水位线)和 HIGH_MARK(高水位线),意义在于避免写缓存使用过多的内存造成 OOM。当 region server 中 memstore 的总大小达到低水位线java_heapsize * hbase.regionserver.global.memstore.size(默认值 0.4) * hbase.regionserver.global.memstore.size.lower.limit(默认值 0.95),region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。直到 region server 中所有 memstore 的总大小减小到上述值以下。当 region server 中 memstore 的总大小达到高水位线java_heapsize * hbase.regionserver.global.memstore.size(默认值 0.4)时,会同时阻止继续往所有的 memstore 写数据。
  • 为了避免数据过长时间处于内存之中,到达自动刷写的时间,也会触发 memstore flush。由 HRegionServer 的属性 PeriodicMemStoreFlusher 控制进行,由于重要性比较低,5min才会执行一次。自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval(默认1 小时)。
  • 当 WAL 文件的数量超过 hbase.regionserver.max.logs,region 会按照时间顺序依次进行刷写,直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下(该属性名已经废弃,现无需手动设置,最大值为 32)。

读流程

在了解读流程之前,需要先知道读取的数据,这就需要了解HFile ;HFile 是存储在 HDFS 上面每一个 store 文件夹下实际存储数据的文件。里面存储多种内容。包括数据本身(keyValue 键值对)、元数据记录、文件信息、数据索引、元数据索引和一个固定长度的尾部信息(记录文件的修改情况)。

键值对按照块大小(默认 64K)保存在文件中,数据索引按照块创建,块越多,索引越大。每一个 HFile 还会维护一个布隆过滤器(就像是一个很大的地图,文件中每有一种 key,就在对应的位置标记,读取时可以大致判断要 get 的 key 是否存在 HFile 中)。KeyValue 内容如下:

  • rowlength -----------→ key 的长度
  • row -----------------→ key 的值
  • columnfamilylength --→ 列族长度
  • columnfamily --------→ 列族
  • columnqualifier -----→ 列名
  • timestamp -----------→ 时间戳(默认系统时间)
  • keytype -------------→ Put

由于 HFile 存储经过序列化,所以无法直接查看。可以通过 HBase 提供的命令来查看存储在 HDFS 上面的 HFile 元数据内容。

# hbase hfile -m -f /hbase/data/命名空间/表名/regionID/列族/HFile 名
hbase hfile -m -f /hbase/data/test/student/ddb70e9c7d26db051de3811155495d51/info/761e9ab212d746b49f4c8b06e775e979

读流程如下

  • 首先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server;
  • 访问对应的 Region Server,获取 hbase:meta 表,将其缓存到连接中,作为连接属性 MetaCache,由于 Meta 表格具有一定的数据量,导致了创建连接比较慢; 之后使用创建的连接获取 Table,这是一个轻量级的连接,只有在第一次创建的时候会检查表格是否存在访问 RegionServer,之后在获取 Table 时不会访问 RegionServer;
  • 创建 Table 对象发送 get 请求。
  • 优先访问 Block Cache,查找是否之前读取过,并且可以读取 HFile 的索引信息和布隆过滤器。
  • 不管读缓存中是否已经有数据了(可能已经过期了),都需要再次读取写缓存和store 中的文件。
  • 最终将所有读取到的数据合并版本,按照 get 的要求返回即可。

合并读取数据优化,每次读取数据都需要读取三个位置,最后进行版本的合并。效率会非常低,所有系统需要对此优化。

  • HFile 带有索引文件,读取对应 RowKey 数据会比较快。
  • Block Cache 会缓存之前读取的内容和元数据信息,如果 HFile 没有发生变化(记录在 HFile 尾信息中),则不需要再次读取。
  • 使用布隆过滤器能够快速过滤当前 HFile 不存在需要读取的 RowKey,从而避免读取文件。(布隆过滤器使用 HASH 算法,不是绝对准确的,出错会造成多扫描一个文件,对读取数据结果没有影响)

文件合并

StoreFile Compaction也即是文件合并,由于 memstore 每次刷写都会生成一个新的 HFile,文件过多读取不方便,所以会进行文件的合并,清理掉过期和删除的数据,会进行 StoreFile Compaction。

Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。MinorCompaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile,并清理掉部分过期和删除的数据,有系统使用一组参数自动控制,Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉所有过期和删除的数据,由参数 hbase.hregion.majorcompaction控制,默认 7 天。

  • Minor Compaction 控制机制:参与到小合并的文件需要通过参数计算得到,有效的参数有 5 个

    • hbase.hstore.compaction.ratio(默认 1.2F)合并文件选择算法中使用的比率。
    • hbase.hstore.compaction.min(默认 3) 为 Minor Compaction 的最少文件个数。
    • hbase.hstore.compaction.max(默认 10) 为 Minor Compaction 最大文件个数。
    • hbase.hstore.compaction.min.size(默认 128M)为单个 Hfile 文件大小最小值,小于这个数会被合并。
    • hbase.hstore.compaction.max.size(默认 Long.MAX_VALUE)为单个 Hfile 文件大小最大值,高于这个数不会被合并。小合并机制为拉取整个 store 中的所有文件,做成一个集合。之后按照从旧到新的顺序遍历。
  • 判断条件为:
    • 过小合并,过大不合并。
    • 文件大小/ hbase.hstore.compaction.ratio < (剩余文件大小和) 则参与压缩。所有把比值设置过大,如 10 会最终合并为 1 个特别大的文件,相反设置为 0.4,会最终产生 4 个 storeFile。不建议修改默认值。
    • 满足压缩条件的文件个数达不到个数要求(3 <= count <= 10)则不压缩。

分区

Region Split也即是分区,Region 切分分为两种,创建表格时候的预分区即自定义分区,同时系统默认还会启动一个切分规则,避免单个 Region 中的数据量太大。

  • 自定义分区:每一个 region 维护着 startRow 与 endRowKey,如果加入的数据符合某个 region 维护的rowKey 范围,则该数据交给这个 region 维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高 HBase 性能。
# 手动设定预分区
create 'student1','info', SPLITS => ['1000','2000','3000','4000']
# 生成 16 进制序列预分区
create 'student2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
# 按照文件中设置的规则预分区,创建 student-splits.txt 文件内容如下:
aaaa
bbbb
cccc
dddd
# 然后执行:
create 'student3', 'info',SPLITS_FILE => 'student-splits.txt'

  • 系统拆分:Region 的拆分是由 HRegionServer 完成的,在操作之前需要通过 ZK 汇报 master,修改对应的 Meta 表信息添加两列 info:splitA 和 info:splitB 信息。之后需要操作 HDFS 上面对应的文件,按照拆分后的 Region 范围进行标记区分,实际操作为创建文件引用,不会挪动数据。刚完成拆分的时候,两个 Region 都由原先的 RegionServer 管理。之后汇报给 Master,由Master将修改后的信息写入到Meta表中。等待下一次触发负载均衡机制,才会修改Region的管理服务者,而数据要等到下一次压缩时,才会实际进行移动。不管是否使用预分区,系统都会默认启动一套 Region 拆分规则。

    • 当 1 个 region 中 的 某 个 Store 下 所 有 StoreFile 的 总 大 小 超 过hbase.hregion.max.filesize (10G),该 Region 就会进行拆分。0.94 版本之后,2.0 版本之前 => IncreasingToUpperBoundRegionSplitPolicy
    • 当 1 个 region 中 的 某 个 Store 下 所 有 StoreFile 的 总 大 小 超 过Min(initialSize*R^3 ,hbase.hregion.max.filesize"),该 Region 就会进行拆分。其中 initialSize 的默认值为 2 * hbase.hregion.memstore.flush.size,R 为当前 Region Server 中属于该 Table 的Region 个数(0.94 版本之后)。

      具体的切分策略为:

      • 第一次 split:1^3 * 256 = 256MB
      • 第二次 split:2^3 * 256 = 2048MB
      • 第三次 split:3^3 * 256 = 6912MB
      • 第四次 split:4^3 * 256 = 16384MB > 10GB,因此取较小的值 10GB
      • 后面每次 split 的 size 都是 10GB 了。2.0 版本之后 => SteppingSplitPolicy
    • Hbase 2.0 引入了新的 split 策略:如果当前 RegionServer 上该表只有一个 Region,按照 2 * hbase.hregion.memstore.flush.size 分裂,否则按照 hbase.hregion.max.filesize 分裂。

JAVA API编程

准备

新建项目后在 pom.xml 中添加下面依赖,注意:会报错 javax.el 包不存在,是一个测试用的依赖,不影响使用。

    <dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>2.5.0</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-b12</version>
</dependency>

在 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>
<description>The directory shared by RegionServers.</description>
</property>
</configuration>

示例

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

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

package cn.itxs.hbasedemo;

import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import java.io.IOException;
public class HBaseConnection {
//声明一个静态属性
public static Connection connection = null;
static{
//1.创建连接配置对象
//Configuration conf = new Configuration();
//2.添加配置参数
//conf.set("hbase.zookeeper.quorum","hadoop102,hadoop103,hadoop104");
//因为已经把配置参数写到hbase-site.xml中,所以直接连接
//3.创建连接
//默认使用同步连接
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();
}
} public static void main(String[] args) throws IOException {
//可以使用异步连接
//CompletableFuture<AsyncConnection> asyncConnection = ConnectionFactory.createAsyncConnection();
//4.使用连接
System.out.println(connection);
//在main最后关闭连接
HBaseConnection.closeConnection();
}
}

封装DDL操作类HBaseDDL.java

package cn.itxs.hbasedemo;

import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException; public class HBaseDDL {
// 添加静态属性 connection 指向单例连接
public static Connection connection= HBaseConnection.connection; /**
* 创建命名空间
* @param namespace 命名空间名称
*/
public static void createNamespace(String namespace) throws IOException {
//1.获取admin
//admin 连接是轻量级的,不是线程安全的 不推荐池化,或者缓存这个连接
Admin admin = connection.getAdmin(); //2.调用方法,创建命名空间
/* 代码相对shel1更加底层 所以shel1能够实现的功能代码一定能实现
11所以需要填写完整的命名空间描述*/ //2.1 创建命名空间描述建造者 => 设计师
NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);
//2.2 给命名空间添加需求
builder.addConfiguration("user","mazankang"); //2.3 使用builder构造出对应的NamespaceDescriptor添加完参数的对象
//完成创建
// 创建命名空间出现的问题 都属于本方法自身的问题 不应该抛出异常 try {
admin.createNamespace(builder.build());
} catch (IOException e) {
System.out.println("命名空间已经存在");
e.printStackTrace();
} //3 关闭admin
admin.close();
} /**
*判断表格是否存在
* @param namespace 命名空间名称
* @param tableName 表格名称
* @return true表示存在
*/
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();
} admin.close(); return b;
} /**
* 创建表格
* @param namespace 命名空间名称
* @param tableName 表格名称
* @param columnFamilys 列族名称 可以有多个
*/
public static void createTable(String namespace,String tableName,String... columnFamilys) throws IOException {
//判断至少有一个列族
if (columnFamilys.length ==0 ){
System.out.println("创建表格需要至少一个列族");
return;
}
//判断表格是否存在
if (isTableExists(namespace,tableName)){
System.out.println("表格已经存在");
return;
} //获取admin
Admin admin = connection.getAdmin(); //2.调用方法创建表格
//2.1创建表格描述的建造者
TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace, tableName)); //2.2添加参数
for (String columnFamily : columnFamilys) {
//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();
} admin.close();
} /**
* 修改表格中一个列族的版本
* @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(); //2.调用方法修改表格
//2.0 获取之前的表格描述
TableDescriptor descriptor = admin.getDescriptor(TableName.valueOf(namespace, tableName));
// 需要填写旧的列族描述
ColumnFamilyDescriptor columnFamily1 = descriptor.getColumnFamily(Bytes.toBytes(columnFamily)); //如果使用填写tableName的方法相当于创建了一个新的表格描述建造者没有之前的信息
//1如果想要修改之前的信息必须调用方法填写一个旧的表格描述
//2.1 创建一个表格描述建造者
//--------------------------
TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(descriptor);
//2.2 对应建造者进行表格数据的修改
//创建列族描述建造者
ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder =
ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1); //修改对应的版本
columnFamilyDescriptorBuilder.setMaxVersions(version); //此处修改的时候 如果是新创建的 别的参数会初始化
tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build()); try {
admin.modifyTable(tableDescriptorBuilder.build());
} catch (IOException e) {
throw new RuntimeException(e);
} //关闭
admin.close();
} /**
* 删除表格
* @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删除表格之前 一定要标记表格为不可用disable
TableName tableName1 = TableName.valueOf(namespace, tableName);
admin.disableTable(tableName1);
admin.deleteTable(tableName1);
} catch (IOException e) {
throw new RuntimeException(e);
}
//关闭admin
admin.close(); return true;
}
}

封装DML读写数据类HBaseDML.java

package cn.itxs.hbasedemo;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.ColumnValueFilter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException; public class HBaseDML {
//添加静态属性connection指向单例属性
public static Connection connection = HBaseConnection.connection; /**
* 插入数据
* @param namespace 命名空间名称
* @param tableName 表名称
* @param rowKye 主键,
* @param columnFamily 列族
* @param columnName 列名
* @param value 值
*/
public static void putCell(String namespace,String tableName,String rowKye,String columnFamily,String columnName,String value) throws IOException {
//获取table
Table table = connection.getTable(TableName.valueOf(namespace, tableName)); //2,调用相关的方法往里面插入数据
//创建put对象
Put put = new Put(Bytes.toBytes(rowKye));
//3,添加属性
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName),Bytes.toBytes(value));
//4.添加对象将对象写入相关的方法
try {
table.put(put);
} catch (IOException e) {
throw new RuntimeException(e);
} table.close();
} /**
* 读取数据 读取对应的一行中的某一列
* @param namespace 命名空间名称
* @param tableName 表名称
* @param rowKye 主键
* @param columnFamily 列族
* @param columnName 列名
*/
public static void getCells(String namespace,String tableName,String rowKye,String columnFamily,String columnName) throws IOException {
//获取table
Table table = connection.getTable(TableName.valueOf(namespace, tableName)); //2.创建get的对象
Get get = new Get(Bytes.toBytes(rowKye));
//如果现在调用get方法读取数据,此时读一整行数据
get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName)); //设置读取数据的版本
get.readAllVersions(); //读取数据得到result对象
Result result = null;
try {
result = table.get(get);
} catch (IOException e) {
throw new RuntimeException(e);
}
//处理数据
///1测试方法:直接把读取的数据打印到空制台
//||如果是实际开发需要再额外写方法对应处理数据
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
//ce11存储数据比较底层
String value = new String(CellUtil.cloneValue(cell));
System.out.println(value);
} //关闭
table.close();
} /**
* 扫描数据
* @param namespace 命名空间名称
* @param tableName 表格名称
* @param startRow 开始的row
* @param stopRow 结束的Row 左闭右开
*/
public static void scanRows(String namespace,String tableName,String startRow,String stopRow) throws IOException {
//获取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)); //读取多行数据 获得scanner
ResultScanner scanner = null;
try {
scanner = table.getScanner(scan);
} catch (IOException e) {
throw new RuntimeException(e);
} //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();
} table.close();
} /**
* 带过滤的扫描
* @param namespace 命名空间名称
* @param tableName 表名称
* @param startRow 开始row
* @param stopRow 结束row
* @param columnFamily 列族
* @param columnName 列名
* @param value 值
* @throws IOException 异常
*/
public static void filterScan(String namespace,String tableName,String startRow,String stopRow
,String columnFamily,String columnName,String value) throws IOException {
//获取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)); //------>>>>>>
//可以添加多个过滤
FilterList filterList = new FilterList();
//创建过滤器
//(1)结果值保留当前列的数据
ColumnValueFilter columnValueFilter = new ColumnValueFilter(
//列族名称
Bytes.toBytes(columnFamily),
//列名
Bytes.toBytes(columnName),
// 比较关系
CompareOperator.EQUAL,
// 值
Bytes.toBytes(value)
);
//(2)结果保留整行数据
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
//列族名称
Bytes.toBytes(columnFamily),
//列名
Bytes.toBytes(columnName),
// 比较关系
CompareOperator.EQUAL,
// 值
Bytes.toBytes(value)
); //filterList.addFilter(columnValueFilter);
filterList.addFilter(singleColumnValueFilter);
//添加过滤
scan.setFilter(filterList);
//读取多行数据 获得scanner
ResultScanner scanner = null;
try {
scanner = table.getScanner(scan);
} catch (IOException e) {
throw new RuntimeException(e);
} //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();
} table.close();
} /**
* 删除一行中的一列数据
* @param namespace 命名空间名称
* @param tableName 表格名字
* @param rowKey 主键
* @param columnFamily 列族
* @param columnName 列名
*/
public static void deleteColumn(String namespace,String tableName,String rowKey,String columnFamily,String columnName) throws IOException {
//获取table
Table table = connection.getTable(TableName.valueOf(namespace, tableName)); //创建delete对象
Delete delete = new Delete(Bytes.toBytes(rowKey)); //添加列信息
//addColumn删除一个版本的数据
//addColumns删除多个版本的数据
delete.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));
delete.addColumns(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName)); try {
table.delete(delete);
} catch (IOException e) {
throw new RuntimeException(e);
} //关闭
table.close();
}
}

创建测试类

package cn.itxs.hbasedemo;

import java.io.IOException;

public class HBaseDemo {
public static void main(String[] args) throws IOException {
// 创建命名空间
HBaseDDL.createNamespace("apitest");
// 判断表是否存在
System.out.println(HBaseDDL.isTableExists("apitest", "student"));
// 创建表
//HBaseDDL.createTable("apitest","student","info","msg");
// 写入数据
HBaseDML.putCell("apitest","student","3001","info","name","hanmeimei"); HBaseDML.getCells("apitest","student","3001","info","name"); HBaseConnection.closeConnection();
}
}

运行测试程序后查看HBase中的数据

**本人博客网站 **IT小神 www.itxiaoshen.com

大数据技术之HBase原理与实战归纳分享-中的更多相关文章

  1. 大数据技术之HBase原理与实战归纳分享-上

    @ 目录 概述 定义 特点 数据模型 概述 逻辑结构 物理存储结构 数据模型 应用场景 基础架构 安装 前置条件 部署 启动服务 高可用 Shell操作 基础操作 命令空间 DDL DML 概述 定义 ...

  2. 大数据技术之HBase原理与实战归纳分享-下

    @ 目录 整合Phoenix 定义 为何要使用 安装 SHELL操作 表的映射 简易JDBC示例 二级索引 二级索引配置文件 全局索引 包含索引 本地索引(local index) HBase与 Hi ...

  3. 大数据技术之HBase

    第1章 HBase简介 1.1 什么是HBase HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储. 官方 ...

  4. 【大数据技术】HBase介绍

    1.HBase简介1.1 Hbase是什么HBase是一种构建在HDFS之上的分布式.面向列.多版本.非关系型的数据库,是Google Bigtable 的开源实现. 在需要实时读写.随机访问超大规模 ...

  5. 大数据技术之_08_Hive学习_05_Hive实战之谷粒影音(ETL+TopN)+常见错误及解决方案

    第10章 Hive实战之谷粒影音10.1 需求描述10.2 项目10.2.1 数据结构10.2.2 ETL原始数据10.3 准备工作10.3.1 创建表10.3.2 导入ETL后的数据到原始表10.3 ...

  6. 《大数据技术应用与原理》第二版-第三章分布式文件系统HDFS

    3.1分布式文件 HDFS默认一个块的大小是64MB,与普通文件不同的是如果一个文件小于数据块的大小,它并不占用整个数据块的存储空间. 主节点又叫名称节点:另一个叫从节点又叫数据节点.名称节点负责文件 ...

  7. 【大数据技术】HBase与Solr系统架构设计

    如何在保证存储量的情况下,又能保证数据的检索速度. HBase提供了完善的海量数据存储机制,Solr.SolrCloud提供了一整套的数据检索方案. 使用HBase搭建结构数据存储云,用来存储海量数据 ...

  8. 《大数据技术应用与原理》第二版-第二章大数据处理架构Hadoop

    2.1概述 Hadoop是Apache旗下的开源分布式计算平台,是基于Java开发的,具有很好的跨平台特性,其中核心文件是MapReduce和HDFS,而HDFS是根据谷歌文件系统GFS开源实现,是面 ...

  9. 【学习笔记】大数据技术原理与应用(MOOC视频、厦门大学林子雨)

    1 大数据概述 大数据特性:4v volume velocity variety value 即大量化.快速化.多样化.价值密度低 数据量大:大数据摩尔定律 快速化:从数据的生成到消耗,时间窗口小,可 ...

随机推荐

  1. Odoo 14 升级模块后为什么template不生效?

    # 升级模块后为什么template不生效? # 直接原因是因为你在record标签的父级data标签标签中设置了noupdate为true.这就导致你后面无论你怎么修改data下面的子标签内容,都不 ...

  2. SDK和API的直接区别

    狭义的说法,在实际工作中, 如果对方需要你提供一个api,是指一个工程提供给另外一个工程的接口(一般是基于http协议). 如果对方需要你提供一个sdk,是指基于对方工程的编程语言,提供一个代码包.在 ...

  3. 10. 选主算法、多版本兼容性及滚动升级 | 深入浅出MGR

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 目录 1. 选主算法 2. 多版本兼容性 3. MGR 5.7滚动升级至8.0 4. 小结 参考资料.文档 免责声明 文章 ...

  4. BTDetect用户手册和技术支持

    BTDetect用户手册和技术支持 1. 程序主要功能 BTDetect是BT(BioTechnology) Detect 生物科技检测的缩写.本程序将根据用户的回答推断其两大基因类型.以及具体的小分 ...

  5. java学习第一天.day01

    Java的编译和运行机制 java文件编译成字节码文件后加载到java缓存中jvm Java的基本语法 1.Java语言严格区分大小写 2.一个Java源文件里可以定义多个Java类,但不能存在多个p ...

  6. @babel/runtime 和 @babel/plugin-transform-runtime 两个的作用是什么

    Babel 最基础的功能就是将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中. 最基础的依赖包也就是 ...

  7. Word 文字错乱,接收方显示的字体与原版不一直

    原版文档使用字体的不是电脑上自带的常规字体,比如,黑软雅黑.黑体.宋体等字体.当把文档发送给其他人查阅时,字体发生了错乱,也就是字体与原版字体不一致. 需要打开"选项"设置,把非常 ...

  8. CAD参数化功能的一些构想

    cad二次开发人员通常对设计的具体业务不够理解,而设计人员对程序开发的学习成本也较高. 可否将cad的参数化功能搭建为二次开发人员与设计人员之间的桥梁. 如设计人员提前利用参数化功能建立对应的动态块, ...

  9. 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(14)-Fiddler断点(breakpoints)实战,篡改或伪造数据

    1.简介 上一篇主要就讲解和分享Fiddler断点的理论和操作,今天宏哥就用具体例子,将上一篇中的理论知识实践一下.而且在实际测试过程中,有时候需要修改请求或响应数据,或者直接模拟服务器响应,此时可以 ...

  10. Postman中的断言

    Postman设置断言 一.断言的定义 1.什么是断言? 一般一个完整的接口测试,包括:请求->获取响应正文->断言,请求和获取响应正文很常见.断言一般是对请求的响应结果做操作,判断预期结 ...