慕课网《HBase 存储原理剖析》学习总结
慕课网《HBase 存储原理剖析》学习总结
3-2 存储目录
第四章:存储设计
4-1 存储思想
- 时间:2018年06月11日星期一
- 说明:本文部分内容均来自慕课网。@慕课网:https://www.imooc.com
- 教学源码:无
- 学习源码:https://github.com/zccodere/study-imooc
课程目标
- HBase的存储模式
- HBase数据表解析
- HBase存储设计
- HBase数据存取解析
行式存储与列式存储介绍
行式存储与列式存储特点
- 行式存储
- 维护大量的索引
- 存储成本高
- 不能够做到线性扩展
- 随机读取效率非常高
- 对事务的支持非常好
- 列式存储
- 根据同一列数据的相似性原理,利于对数据进行压缩
- 存储成本低
- 由于每列数据分开存储,可以并行查找多列的数据
行式存储与列式存储场景
- 行式存储
- 表与表之间有关联关系,数据量不大(小于千万量级)
- 强事务关联的特性
- 列式存储
- 对于单列或者相对比较少的列获取频率较高
- 针对多列查询,使用并行处理的查询
- 利于数据压缩和线性扩展的存储
- 事务使用率不高,读取的场景频率不高,同时数据量非常大
- 随机更新某一行的频率不高
列簇式存储:概念
- 列簇(多个数据列的组合),HBase表中的每个列都归属于某个列簇
- 列簇是表的schame的一部分,但是列并不是
- 创建表时,需要给出列簇的名称,不需要给出列的名称
- 列名都是以列簇作为前缀
- 访问控制磁盘和内存的使用统计都是在列簇层面进行
- HBase准确的说是列簇数据库,而不是列数据库
- 列簇数据库将列组织为列簇,每列都必须是某个列簇的一部分
- 访问数据的单元也是列
HBase表的组成
- Table = RowKey + Family + Column + Timestamp + Value
- RowKey :HBase中用RowKey去标识唯一的一行数据,一行数据中包含多个列簇
- Family:多个列簇。每一列簇包含多个列
- Column:列标识符。每一列数据包含了版本和值
- Timestamp:版本。可以理解为时间戳,也可以理解为一个数据的版本
- Value:数据值。数据本身的值
HBase数据存储的模式
- (Table, RowKey, Family, Column, Timestamp)-> Value
- 其实就是HBase表反过来看的样子
- 【重点】更抽象一点,其实HBase表数据就是Key-Value结构的
图解
列簇式存储:列数据属性
HBase重要特性:列数据版本的概念,默认一列数据可以保存三个版本
列簇式存储:数据存储原型
2-2 存储示例示例表定义
示例表数据
第三章 表的解析 3-1 建表语句示例表语句定义
压缩算法
算法 | 压缩率 | 编码速度 | 解码速度 |
---|---|---|---|
GZip | 13.4% | 21MB/s | 118MB/s |
LZO | 20.5% | 135MB/s | 410MB/s |
Snappy | 22.2% | 172MB/s | 409MB/s |
在hbase-site.xml
文件中配置或查看存储目录的节点
<property>
<name>hbase.rootdir</name>
<value>/home/hbase_data</value>
</property>
进入到HBase系统目录
- .tmp
- 当对表做创建或删除操作时,将表移动到tmp目录下,然后再进行处理
- 临时交换的表,临时存储一些当前需要修改的数据结构
- WALs
- 预写日志,被HLog实例管理的WAL文件
- 可以理解为存储HBase的日志,HBase分布式数据库系统的操作日志
- archive
- 存储表的归档和快照
- HBase在做分割或合并操作完成后,会将Hfile文件移动到该目录中,然后将之前的Hfile删除掉
- 是由Master上的定时任务定期去处理,这个目录的作用可以简单理解为去管理HBase的数据
- corrupt
- 用于存放损坏的日志文件,一般是空的
- data
- HBase存储数据的核心目录
- 系统表和用户表数据都存储在这里
- hbase.id
- HBase启动运行后,是集群中的唯一ID,用来标识HBase进程用的
- hbase.version
- 表明了集群的文件格式版本信息
- 其实就是表明了Hfile的版本信息
- oldWALs
- 备份WALs中的日志文件
data目录解析
HBase元信息表
Row Key | Value |
---|---|
table、key、time | region server |
HBase中的LSM存储思想
- 什么是LSM树
LSM日志结构合并树,有两个或两个以上存储数据的结构组成的,每一个数据结构各自对应自己的存储介质
- LSM树的简易模型描述
- LSM思想在HBase中的思想
HBase数据存储模块简介
RegionServer = Region + Store + MemStore + StoreFile + HFile + HLog
HBase Region解析
- 什么是Region
每一个Region都会存储于确定的RegionServer上
- Region的特点
- 是HBase中分布式存储和负载均衡的最小单元
- Region的数据不能低于集群中节点的数量
- RegionServer对Region进行拆分
- 尽量让Row key分散到不同的Region
HBase HFile解析
- Store + MemStore + StoreFile
- Store与列簇是一对一的关系
- MemStore是一个内存数据结构,保存修改的数据
- StoreFile是由内存数据写入到文件后形成的
- HFile 文件
- 是HBase存储数据文件的最基本的组织形式
- 底层是Hadoop的二进制格式文件
- 是用户数据的实际载体,存储Key-Value的数据
- Scanned block section:会被读取,主要是存储用户数据
- Nonscanned block section:不会被读取,主要包含元数据块
- Load-on-open section:RegionServer启动时加载,主要是HFile的元数据
- Trailer:HFile的基本信息,HFile元数据的一部分
- Data Block
- HBase中数据的最基本的存储单元
- 是实际存储用户数据的数据结构
- 包含很多Key-Value
HBase WAL解析
- 简介介绍WAL(预写日志)
- WAL最重要的功能就是灾难恢复
- WAL解决了什么问题:HA(高可用)问题
- 怎么解决:远程备份
- HLog
- WAL是通过HLog模块实现的
- HLog是什么:HLog是实现WAL的类,一个RegionServer对应一个HLog实例
- HLogKey
- WAL使用Hadoop的序列化文件将记录存储为Key-Value的数据集,Key就是HLog的Key
- WAL使用Hadoop的序列化文件将记录存储为Key-Value的数据集,Key就是HLog的Key
- HLogSyncer
- 是日志同步刷写类
- 是日志同步刷写类
- HLogRoller
- 特点的时间去滚动日志,形成新的日志,避免单个日志文件过大
- 根据HLog的序列化的number对比已经持久化的HFile的序列号,删除旧的,不需要的日志
HBase Compaction解析
- 什么是Compaction
- Compaction会从一个Region的Store中选择一些HFile文件进行合并
- 为什么要Compaction
- 随着系统不停的刷写,会导致存储目录中有过多的数据文件
- Compaction分类
- MinorCompaction:小合并
- MajorCompaction:大合并
- Compaction触发时机
- MemStore 内存数据写入到硬盘上
-
HBase数据存储流程解析
- HBase Client
- 请求Zookeeper,确定 MetaTable所在RegionServer的地址
- 在根据RowKey找到归属的RegionServer
- HBase Client Put(Delete)数据,提交到RegionServer
- HBase Server
- Region Server 去获取行锁,Region更新共享锁
- 写HLog,WAL
- 写缓存,MemStore
- 将日志同步到HDFS
- 写满缓存后,启动异步线程将数据写入到硬盘上
- 可能触发Compaction或拆分
HBase数据获取流程解析
- HBase Client
- 请求Zookeeper,确定 MetaTable所在RegionServer的地址
- 去对应的RegionServer地址拿到对应数据
- HBase Server
- Region Server 构建RegionScanner准备进行检索
- 有多少个列簇就构建多少个StoreScanner,用于对确定的列簇数据检索
HBase数据存取api介绍
新增api
void put(Put put)throws IOException
Put构造方法
Put(byte[] row)
Put(byte[] row,long ts)
填充值
Put add(byte[] family,byte[] qualifier,byte[] value)
Put add(byte[] family,byte[] qualifier,long timestamp,byte[] value)
Put add(KeyValue kv)throws IOException
原子检查写
boolean checkAndPut(add(byte[] row,byte[] family,byte[] qualifier,byte[] value,put))
删除api
void delete(Delete delete)throws IOException
构造方法
Delete(byte[] row)
填充值
Delete deleteFamily(byte[] family)
Delete deleteColumns(byte[] family,byte[] qualifier)
Delete deleteColumns(byte[] family,byte[] qualifier,long timestamp)
原子检查删除
boolean checkAndDelete(byte[] row,byte[] family,byte[] qualifier,byte[] value,Delete delete)throws IOException
获取api
Result get(Get get)throws IOException
构造方法
Get(byte[] row)
填充值
Get addFamily(byte[] family)
Get addColumn(byte[] family,byte[] qualifier)
Get setTimeRange(long minStamp,long maxStamp)throws IOException
Get setTimeStamp(long timestamp)
Get setMaxVersions()
Get setMaxVersions(int maxVersions)throws IOException
第五章:案例演示
5-1 案列演示
创建名为hbase-demo的maven工程pom如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myimooc</groupId>
<artifactId>hbase-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.7</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>spring-boot-starter-hbase</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
1.编写User类
package com.myimooc.hbase.demo.dto;
/**
* <br>
* 标题: 用户表<br>
* 描述: 用户表<br>
*
* @author zc
* @date 2018/06/21
*/
public class User {
/**
* 行键
*/
private String row;
/**
* 基础信息
*/
private BaseInfo baseInfo;
/**
* 其他信息
*/
private OtherInfo otherInfo;
public User() {
}
public User(String row, BaseInfo baseInfo, OtherInfo otherInfo) {
this.row = row;
this.baseInfo = baseInfo;
this.otherInfo = otherInfo;
}
@Override
public String toString() {
return "User{" +
"row='" + row + '\'' +
", baseInfo=" + baseInfo +
", otherInfo=" + otherInfo +
'}';
}
public String getRow() {
return row;
}
public void setRow(String row) {
this.row = row;
}
public BaseInfo getBaseInfo() {
return baseInfo;
}
public void setBaseInfo(BaseInfo baseInfo) {
this.baseInfo = baseInfo;
}
public OtherInfo getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(OtherInfo otherInfo) {
this.otherInfo = otherInfo;
}
/**
* 使用内部类表示 b 列簇 基础信息
*/
public static class BaseInfo {
/**
* 名称
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private String sex;
public BaseInfo() {
}
public BaseInfo(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/**
* 使用内部类表示 o 列簇 其他信息
*/
public static class OtherInfo {
/**
* 电话
*/
private String phone;
/**
* 地址
*/
private String address;
public OtherInfo() {
}
public OtherInfo(String phone, String address) {
this.phone = phone;
this.address = address;
}
@Override
public String toString() {
return "OtherInfo{" +
"phone='" + phone + '\'' +
", address='" + address + '\'' +
'}';
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}
2.编写UserRowMapper类
package com.myimooc.hbase.demo.mapper;
import com.myimooc.hbase.demo.dto.User;
import com.spring4all.spring.boot.starter.hbase.api.RowMapper;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
/**
* <br>
* 标题: 用户表ORM<br>
* 描述: 用户表ORM<br>
*
* @author zc
* @date 2018/06/21
*/
public class UserRowMapper implements RowMapper<User> {
private static byte[] FAMILY_B = "b".getBytes();
private static byte[] FAMILY_B_NAME = "name".getBytes();
private static byte[] FAMILY_B_AGE = "age".getBytes();
private static byte[] FAMILY_B_SEX = "sex".getBytes();
private static byte[] FAMILY_O = "o".getBytes();
private static byte[] FAMILY_O_PHONE = "phone".getBytes();
private static byte[] FAMILY_ADDRESS = "address".getBytes();
@Override
public User mapRow(Result result, int i) throws Exception {
User.BaseInfo baseInfo = new User.BaseInfo(
Bytes.toString(result.getValue(FAMILY_B, FAMILY_B_NAME)),
Bytes.toInt(result.getValue(FAMILY_B, FAMILY_B_AGE)),
Bytes.toString(result.getValue(FAMILY_B, FAMILY_B_SEX)));
User.OtherInfo otherInfo = new User.OtherInfo(
Bytes.toString(result.getValue(FAMILY_O, FAMILY_O_PHONE)),
Bytes.toString(result.getValue(FAMILY_O, FAMILY_ADDRESS)));
return new User(Bytes.toString(result.getRow()),baseInfo,otherInfo);
}
}
3.编写HbaseService类
package com.myimooc.hbase.demo.service;
import org.apache.hadoop.hbase.client.Mutation;
import java.util.List;
/**
* <br>
* 标题: HBase服务<br>
* 描述: HBase服务<br>
*
* @author zc
* @date 2018/06/21
*/
public interface HbaseService<T> {
/**
* 查询指定行键的表数据
* @param tableName 表名
* @param row 行键
* @return 数据
*/
T findByRow(String tableName,String row);
/**
* 查询指定行键之间的表数据
* @param tableName 表名
* @param startRow 开始行键
* @param endRow 结束行键
* @return 数据集合
*/
List<T> findByStartEndRow(String tableName,String startRow,String endRow);
/**
* 保存或修改数据
* @param tableName 表名
* @param datas 数据集合
* @return 数据集合
*/
List<Mutation> saveOrUpdate(String tableName,List<Mutation> datas);
}
4.编写UserHbaseServiceImpl类
package com.myimooc.hbase.demo.service.impl;
import com.myimooc.hbase.demo.dto.User;
import com.myimooc.hbase.demo.mapper.UserRowMapper;
import com.myimooc.hbase.demo.service.HbaseService;
import com.spring4all.spring.boot.starter.hbase.api.HbaseTemplate;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <br>
* 标题: 用户服务<br>
* 描述: 用户服务<br>
*
* @author zc
* @date 2018/06/22
*/
@Service
public class UserHbaseServiceImpl implements HbaseService<User> {
@Autowired
private HbaseTemplate hbaseTemplate;
@Override
public User findByRow(String tableName, String row) {
return hbaseTemplate.get(tableName, row, new UserRowMapper());
}
@Override
public List<User> findByStartEndRow(String tableName, String startRow, String endRow) {
Scan scan = new Scan(Bytes.toBytes(startRow), Bytes.toBytes(endRow));
return hbaseTemplate.find(tableName, scan, new UserRowMapper());
}
@Override
public List<Mutation> saveOrUpdate(String tableName, List<Mutation> datas) {
hbaseTemplate.saveOrUpdates(tableName, datas);
return datas;
}
}
5.编写application.properties
# Zookeeper 地址
spring.data.hbase.quorum=192.168.0.104:2181
# HBase 存储路径
spring.data.hbase.rootDir=hdfs://zccoder.com:9000/hbase/
# HBase 在 Zookeeper上的根节点名称
spring.data.hbase.nodeParent=/hbase
6.编写Application类
package com.myimooc.hbase.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
7.编写UserHbaseServiceImplTest类
package com.myimooc.hbase.demo.service;
import com.alibaba.fastjson.JSON;
import com.myimooc.hbase.demo.Application;
import com.myimooc.hbase.demo.dto.User;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
/**
* <br>
* 标题: 单元测试类<br>
* 描述: 单元测试类<br>
*
* @author zc
* @date 2018/06/22
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,classes = Application.class)
public class UserHbaseServiceImplTest {
@Autowired
private HbaseService<User> service;
/**
* 数据表名称
*/
private String tableName;
@Before
public void init(){
tableName = "demo:user";
}
@Test
public void testFindByRow(){
System.out.println(JSON.toJSONString(service.findByRow(tableName,"root")));
}
@Test
public void testFindByStartEndRow(){
System.out.println(JSON.toJSONString(service.findByStartEndRow(tableName,"r","z")));
}
@Test
public void testSaveOrUpdate(){
List<Mutation> datas = new ArrayList<>();
Put put= new Put(Bytes.toBytes("root"));
put.addColumn(Bytes.toBytes("b"),Bytes.toBytes("name"),Bytes.toBytes("imooc"));
put.addColumn(Bytes.toBytes("b"),Bytes.toBytes("age"),Bytes.toBytes("18"));
put.addColumn(Bytes.toBytes("b"),Bytes.toBytes("sex"),Bytes.toBytes("m"));
put.addColumn(Bytes.toBytes("o"),Bytes.toBytes("phone"),Bytes.toBytes("123456789"));
put.addColumn(Bytes.toBytes("o"),Bytes.toBytes("address"),Bytes.toBytes("北京市朝阳区"));
datas.add(put);
// Delete delete = new Delete(Bytes.toBytes("root"));
// datas.add(delete);
List<Mutation> results = service.saveOrUpdate(tableName,datas);
System.out.println(results);
}
}
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦