17道题你能秒我?我Hbase八股文反手就甩你一脸

2021-07-12 16:03:24 浏览数 (1)

技术背景

起源于谷歌旧三篇论文中bigtable。

设计目的

为了解决大数据环境中海量结构化数据的实时读写问题。为了弥补hadoop生态中没有实时存储的缺陷。

设计思想

分布式

技术本质
  • 概念:分布式列存储nosql数据库
  • 解释:

列存储:底层数据文件存储格式是列式存储

nosql:非关系型,可以存储结构化和半结构化数据

数据库:符合数据库的特征

核心特性

大表: 为存储海量数据而生,亿级别的行和百万级别的列

分布式:

1.分布式内存:为实现实时读写,将数据放在内存中

2.分布式外存:内存中放置不了的数据,刷写到hdfs上

多版本:一个列簇允许设置版本数,即允许一个数值存储多个版本,通过"ns:tb cf:c ts"唯一缺点一条数据

nosql:

1.RDBMS:关系型数据库,例如"mysql,oracle,postgresql,sqlserver"

2.nosql:非关系型数据库,例如"hbase,redis,mongodb"

随机实时读写

1.数据优先存储在内存中,命中率高数据

2.命中率中等的数据,存在缓存中

3.命中率低的数据,存在hdfs上

代码语言:javascript复制
依赖于hdfs:将海量的数据存到hdfs上,即保证了数据了安全性,又保证了数据的持久化
不支持join操作,只支持行级事务,复杂join操作需要集成hive
只支持字节数组类型
稀疏:对于空null的列,并不占用存储空间,因此表的设计可以非常稀疏
集群角色

物理架构

client

种类:

  • bin/hbase shell:不支持sql语句,可以使用help 'command',exit,
  • java api:

hue:hbase实现了ThriftServer服务端,用于提供hue客户端连接,thriftserver来实现整个过程的访问以及执行

sql on hbase:底层使用的mapreduce操作hbase

zookeeper

概念:hbase中的zk被称为真·主节点

职责:

  • 辅助集群的主节点选取,实现HA机制
  • 为集群的访问提供入口,存贮所有Region 的寻址入口
  • 负责存储hbase中几乎所有的元数据,如节点信息,master的主备节点,regionserver的节点,表的状态
  • 负责实现系统容错,若发现regionserver注册节点消失,即认为此regionserver故障,需要通知hmaster启动恢复程序
  • 负责存储meta表的region[rootregion]所在的regionserver地址
  • 负责flush,splitWAL的状态等
  • 存储Hbase 的schema,包括有哪些table,每个table 有哪些column family

内容:

每一个节点都会在zookeeper上去注册

zookeeper存储了meta表的region信息,客户端通过访问zookeeper才能请求集群

hdfs:

职责:存储hfile,wal

Hmaster:

概念:辅助主节点

职责:

  • 负责管理所有从节点信息,
  • 负责region在regionserver上的分配,实现regionserver的负载均衡,
  • 负责region的数据迁移,例如分裂场景
  • 负责meta表以及元数据的修改,meta表不等于元数据,可将meta表看做元数据的一部分,meta表只是存储region的信息,而元数据包括regionserver,region,表,namespace等信息
  • 负责接收客户端的DDL请求,用于更改元数据,修改zookeeper中的存储
  • 通过zookeeper监听每个regionserver的状态,如果regionserver故障,就会启动恢复regionserver的进程
  • 发现失效的region server 并重新分配其上的region
  • HDFS 上的垃圾文件回收
  • 处理schema 更新请求

RegionServer:

职责:

  • 负责接收并处理所有客户端的数据的读写请求
  • 负责管理该节点上所有的region,处理对这些region 的IO 请求
  • 负责记录WAL
  • 负责读缓存:memcache,占堆内存40%,并刷新缓存到HDFS
  • 执行压缩
  • 负责处理Region 分片

补充:

  • regionserver就是从节点,有多台
  • 一台regionserver中管理很多个不同的表分区【region】
  • meta表中的数据也存在regionserver中

Hlog:

概念:预写日志,类似于hdfs上的edits文件,也类似于mysql中的binlog文件。

补充:

每个Region Server 维护一个Hlog,而不是每个Region 一个。这样不同region(来自不同table)的日志会混在一起,这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高对table 的写性能。带来的麻烦是,如果一台region server 下线,为了恢复其上的region,需要将regionserver 上的log 进行拆分,然后分发到其它region server 上进行恢复,HLog文件就是一个普通的Hadoop Sequence File。

Region:

概念:

regionserver中的数据存储单元,不是region级别的

补充:

  • 一张表将数据按照rowkey的范围来划分到多个region中,以此来散列表的数据,
  • 默认情况下创建的表只有一个region
  • 同一张表的不同region的范围总和是从负无穷到正无穷
  • 一个region只会被一台regionserver管理
  • region是整个hbase中最小的负载均衡单元,是实现分布式的基本
  • region的分配不均衡会导致数据倾斜及热点问题
  • 用户读写时,会根据rowkey进行判断,从哪个分区读取,从哪个分区写入
  • 每一个分区是一个目录
  • store个数/2 = region个数

Store

意义:设计列簇时将具有相同IO属性的列放在同一个列簇里,因为底层存储是一个列簇存储独占一个store;放在一个storefile中,可以提供快速的读写操作

概念:

region是表的划分,store是列簇的划分,机器内存 * 40% /128M=store个数

MemStore

功能:

补充:

属于Store中的内存存储区域。每个store中都有一个memstore,用于存储刚写入的数据。写内存,占堆内存的40%,

StoreFile /HFile

功能:

解释:

1.memstore满足条件会进行flush,刷写出来的数据文件就是storefile,存储在hdfs上被称为hfile 2.一个store中会有多个storefile文件,但最终会优化合并成一个文件【二进制文件】 3.storefile是hfile的封装,也可认为是同一个文件,不同的称呼

补充:

HFile分为六个部分:

1.Data Block 段–保存表中的数据,这部分可以被压缩

2.Meta Block 段(可选的)–保存用户自定义的kv 对,可以被压缩。

3.File Info 段–Hfile 的元信息,不被压缩,用户也可以在这一部分添加自己的元信息。

4.Data Block Index 段–Data Block 的索引。每条索引的key 是被索引的block的第一条记录的key。

5.Meta Block Index段(可选的)–Meta Block 的索引。

6.Trailer–这一段是定长的。保存了每一段的偏移量

逻辑存储

NameSpace

名称空间,物理存在。默认创建时就已经存在两个名称空间default和hbase。属于表的一部分,用于区分表的存储,每个表必定有自己是名称空间,若不指定,默认属于default

Table

表,访问时通过namespace:table_name来访问这张表,不加默认访问是default下的表

Rowkey

行键,通过rowkey可以唯一指定一行的数据(注意不是一条),在创建表时就已经存在,不需要再次创建。

rowkey的设计,影响了底层region的节点数据分配,会造成热点问题,影响读写性能。

rowkey是整个hbase中的唯一索引,在hbase中不支持自定义索引,处理rowkey之外,没有别的索引,查找数据时,要么通过rowkey进行查找,要么全表扫描,区别于mysql中可以自定义索引。

ColumnFamily

列簇,列的分组,一个列簇下有多个列,每个列必然属于某个列簇,可通过列簇指定这一个列簇中所有列的版本号。一般将具有相同io属性的字段放入同一个列簇。创建表时必须指定至少一个列簇。一张表一般不建议超过两个列簇,一般在一到两个列簇,超过两个之后,hbase的读写性能会降低。

Column

列,列标签,一个列簇下有多个列,一个列必属于某个列簇,两个列簇中的列个数不必相等。两个行同一列簇下运行有不同的列,即半结构化。通过cf:c 唯一指定一列。

Value

值,每一列的值,hbase底层没有数据类型,都是字节存储,允许存储多版本的值

Version

版本数,属于列簇的一个属性,默认版本为1个,可以配置多版本来存储多版本的数据 默认查询时,只显示每一列的最新的那个版本数据 如果每条存储数据超过版本数,旧的版本会被覆盖 设计多版本是为了存储,默认显示最新版本是为了读取

TimeStamp

时间戳,用来区分版本, version版本数是一个设置,timestamp是一个标识 默认值是数据插入的格林尼治时间 查询是需要指定时间戳才能唯一确定一条数据 每一列都有一个时间戳,同行同列簇同版本下的不同列的时间戳相同

列存储

概念:

区别于rdbms的行式存储格式,nosql使用列式存储格式。

优点:

行式存储操作实时行级别的,列式存储操作是列级别的。所以粒度更细 行式存储一行中的列都是一样的(结构化),列式存储每一行中的列可以不一样(半结构化)。所以支持的数据格式更广泛,半结构化可以支持结构化数据,结构化数据不可以支持半结构化数据 性能更优化,读写速度更快

DDL
代码语言:javascript复制
1. namespace
    list_namespace
    create_namespace
    drop_namespace
    describe_namespace
    list_namespace_tables

2. ddl
备注:只有管理员admin才能操作
    list
    create:创建表时只需要给定表名和列簇名称,两种方式:配置列簇属性和不配置列簇属性
    describe/desc
    drop:hbase中表是enable状态的话,不允许对表结构进行修改和删除,需要先disable禁用
    disable
    enable
DML
代码语言:javascript复制
1.dml
    put:hbase中没有更新和删除操作,所有的更新都是插入
    scan:用于查询一行或多行,或全表扫描
    get:用于查询某一行,就是某一个rowkey的数据
    delete
热点问题与数据倾斜

概念

热点问题:客户端的请求大量集中在某一个节点,导致该节点上的负载非常高,而其他节点负载过低的-

现象

数据倾斜:由于计算任务大量集中在某一个节点,导致该节点上的负载非常高,而其他节点负载过低的现象

区别

  • 热点问题:请求层面,热点问题可能会造成数据倾斜
  • 数据倾斜:计算层面,数据倾斜不一定由热点问题造成

原因

热点问题在很多场景中都会发生,在hbase中表现造成热点问题的原因是:

1.客户端大量请求的rowkey连续集中在某一个region上 2.表中只有一个分区,所有的请求都集中在这个region上

解决

1.需要设计好表的分区region范围,避免分区范围不均衡,通过分区范围解决 2.需要创建预分区,多分区,通过分区个数解决

预分区

背景:在创建表的时候,默认只有一个分区,此时容易操作服务端热点问题

概念:在创建表的时候,根据rowkey的分布来设计一张表刚开始就有多个分区,分区需要根据rowkey的前缀或者完整的rowkey来设计,因为region的分配就是根据rowkey来划分分区的

意义:

1.增加数据读写效率

2.负载均衡,防止数据倾斜

3.方便集群容灾调度region

4.优化Map 数量

方式:

1.指定分区范围 create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']

2.适用于分区较多场景 指定规则文件 create 'ns1:t2', 'f1', SPLITS_FILE => '/export/datas/splits.txt'

3.通过java API 来划分分区admin.createTable(HtableDescriptor desc,byte splitKeys)

RowKey的设计规则

原因:为什么rowkey设计至关重要?

  • 所有数据在存储时都是根据rowkey来读写对应分区的,即分区是根据rowkey来划分的。
  • rowkey是hbase中的唯一索引。
  • rowkey在查询时是前缀匹配的。

后果:rowkey设计不好会产生什么后果?

  • 会造成热点问题
  • 会造成全表扫描

设计规则:

唯一原则:任何一个rowkey,必须唯一标记一行,类似于主键的唯一性

散列原则:

构建rowkey的随机散列,不允许rowkey是连续的,避免热点问题。

若rowkey前缀是一个固定的字段,且会产生连续,如何解决呢?

  1. 编码,通过md5/crc32等方式来将固定字段进行编码然后作为rowkey
  2. 反转,rowkey是按照ascii码的字典顺序,固定字段反转之后,就不连续
  3. 随机数,将固定字段加随机数,不推荐,因为不便于维护

业务原则:根据业务维度来设计rowkey,例如将经常作为查询条件的维度

组合原则:将各个业务维度组合来设计rowkey

长度原则:一般rowkey的长度不要超过100个字节,可以使用十六进制或三十二进制压缩长度

JavaAPI

代码语言:javascript复制
介绍:
    1.hbase中 hbase shell 一般用来封装脚本,执行ddl操作,一般用来封装做ddl操作通过hbase shell  xxx.query指定,脚本操作结尾要加exit
    2.javaapi则是实际使用场景,一般用来执行dml操作,和分布式框架使用,做中间件技术,提供程序访问hbase的接口

API方法:
    1.HBaseConfiguration; 创建hbase的初始化对象
    2.HBaseAdmin;hbase的管理员对象,需要进行ddl操作时需要的对象
        1.tableExists
        2.disableTable
        3.deleteTable
        4.deleteNamespace
        5.listNamespaceDescriptors
    3.HTableDescriptor;hbase的表描述对象
        1.addFamily
        2.createTable
        3.getTableName
    4.TableName;表名称对象
        1.getNameAsString
    5.HColumnDescriptor;hbase的列簇描述对象
        1.setMaxVersions
        2.setBlockCacheEnabled
        3.
    6.NamespaceDescriptor;hbase的名称空间描述对象
        1.createNamespace
        2.create
    7.Get;get命令获取数据
    8.Result;一个result代表一个rowkey的所有数据
        1.rawCells
    9.Cell;一个cell就是一列的数据
    10.Table;表对象
        1.get
        2.put
        3.delete
        4.getScanner
    11.Put;
        1.addColumn
    12.Delete;
        1.addColumn
    13.ResultScanner;result对象的集合
读取
代码语言:javascript复制
1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablemapper(输入和map的参数);
2.mapper类,需要继承TableMapper类,默认input输入的key和value为ImmutableBytesWritable和Result

实现:
    底层通过tableinpputformat实现;sqoop底层通过dbinputformat实现

<解释>
    因为mr读取hbase数据,只是做了数据的读取,并不做数据的聚合,所有不需要reduce
写入
代码语言:javascript复制
1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablereduce(reduce和输出的参数);
2.reducer类,需要继承TableReducer类,默认output输出的value为put对象

实现:
    底层通过TableOutputFormat实现;sqoop底层通过dboutputformat实现

<解释>
    需要在reduce类中构建put对象,用于存储数据,所以需要reduce
Filter 过滤器

背景知识:

  • 在hbase中,所有数据在存储时按照rowkey的ASCII码表字典顺序进行排序的
  • 在hbase中,rowkey的查询都是前缀匹配
  • rowkey是hbase中的唯一索引,所有数据的读写要么通过rowkey查询,要么就是全表扫描
  • hbase底层没有数据类型,数据全部都是按照字节数组存储,
  • 按照keyvalue对格式存储每一列数据,key=ns tb cf c ts,value=value
  • 一般搭配scan来使用
代码语言:javascript复制
 shell过滤器种类:
    1. TimestampsFilter,时间戳过滤器
    2. QualifierFilter,列名过滤器,需要指定比较参数和比较值 QualifierFilter (>=, 'binary:xyz')
    3. ROWPREFIXFILTER,rowkey前缀过滤器

 javaapi过滤器种类:
       第一类:比较过滤器;通过比较的工具类,来实现过滤,返回符合的rowkey所有的数据
            1.RowFilter
            2.FamilyFilter
            3.QualifierFilter
            4.ValueFilter
       第二类:专有过滤器;用的比较多,封装好的方法来实现
             1.prexfixFilter:rowkey前缀过滤器
             2.singleColumnValueFilter/singleColumnValueExcludeFilter:单列值过滤器
             3.MultipleColumnPrefixFilter:多列过滤器
             4.pageFilter:分页过滤器,一般在工作中,必须指定对应的起始位置,一般搭配startrow来使用
      第三类:组合过滤器,就是各种过滤器组合在一起,FilterList


如何使用过滤器:
    1.shell中使用"{列属性}"指定,例如:
        {COLUMNS => ''};{STARTROW => '闭区间',STOPROW => '开区间'}
        {ROWPREFIXFILTER => '',FILTER => ''}
Hbase:ns/Hbase:meta

"hbase:namespace"表,这张表会记录hbase中所有namespace的信息,当用户进行请求时,会读取这张表的信息来判断用户访问的namespace是否存在

"hbase:meta"表,hbase表中的元数据表,meta表中记录了:

meta表中的rowkey包含了regioninfo和server两个部分,其中包含regioninfo的rowkey,记录了region的名称,startkey和stopkey。包含server的rowkey记录了这个region所在的regionserver的地址和端口。

meta表记录了除了自己之外所有的在hbase中的表的region的名称;其中“region名称”的构成:表名称 region的起始范围 region的时间 region的唯一id。

补充:

1.数据读写请求,客户端如何找到对应的region的region的?

A:zk记录了meta表的"region信息","region信息"包括region的名称,startkey和stopkey,还包含了这个region所在的节点地址和端口,通过这些去对应regionserver上找到的meta表的数据,然后meta表中记录了除了自己之外的hbase中所有表的"region信息",这些"region信息"也包括region的名称,startkey和stopkey,还包含了这个region所在的节点地址和端口。然后通过这些信息再去找到对应regionserver上的region

Hbase的存储原理

读写请求概述:

  • 客户端第一次请求时,本地没有记录region信息,先去请求zk,获取meta表的region信息。
  • 然后再跟据region信息请求对应的regionserver,获取meta表的数据,得到要查询表中rowkey对应的region信息。
  • 客户端再次跟据返回的表中rowkey对应的region信息去请求regionserver提交读写请求,并缓存本地请求的地址及region信息,供下次使用,直接请求regionserver获取表数据。
  • regionserver跟据请求来操作region,跟据列簇来判断读写哪个store。

读请求:

请求会优先读取内存中数据,即memstore,若数据没有在内存中,就读缓存,然后读hdfs

写请求:

  • 请求会先写入内存memstore中,当memstore满足条件时,触发flush刷写,将memstore中的数据变成storefile文件
  • 通过hbase底层封装的hdfs客户端,将文件生成在以hfile的文件类型存储在hdfs上
Hbase读写请求流程

写请求流程

  • 客户端提交一条写的命令,根据这个请求的rowkey来获取对应的region信息,来请求对应的regionserver,以此来对rowkey进行操作
  • 将请求提交给对应的regionserver,regionserver根据请求写入的region的名称来操作
  • 根据请求中的列簇来判断要写入该region中的哪个store
  • 将数据以keyvalue形式写入memstore
  • memstore达到一定条件以后,会触发flush,内存中数据会写入hdfs,
  • hdfs中的storefile达到一定条件以后,会触发compaction,将storefile文件归并排序进行合并
  • 当整个region的数据大小达到一定条件以后,会触发split,一个region会分裂成两个region,原来的region被销毁

读请求流程

客户端请求zk,从zk获取meta表的地址

客户端读取meta表的数据,根据读命令中的表名来获取表所有的reigon信息,

找到要读取的region以及对应的regionserver地址

如果是get请求,指定了rowkey,则返回这个rowkey对应的region信息

如果是scan请求,则返回这张表对应的所有的region信息

根据返回的reigon地址,请求对应的regionserver,

regionserver根据region名称来找到对应的region,并进行读操作

若请求中指定了列,则读取列簇对应的store,若请求中没有指定列,则读取所有store的数据

请求在读取store时,优先读取内存中的store,即memstore,若memstore中没有找到,若开启了缓存,则取memcache缓存中找,若没有开启缓存,则直接去hdfs上找storefile,若hdfs上也没有,就是数据不存在,直接返回,若是hdfs上有,就将这条数据返回,若开启了缓存,还需要将数据添加到memcache中,方便下次使用

代码语言:javascript复制
查询内存:
      有:返回
      没有:缓存:
              有:返回
              没有:hdfs
                   有:返回并添加缓存
                   没有:返回

解释:

  1. 经实践分析,新写入的数据在请求访问是命中率最高,所以保存在内存中
  2. 根据LRU最近最少使用算法,将访问频率高的数据添加到缓存中,提高缓存访问的命中率
LSM-tree模型

概念:Log-Struct-Merge 模型树,即wal,flush,compaction,split等过程;

功能:将随机无序的数据变成有序的数据,通过有序的算法来加快数据的读取,因为写入时需要进行排序,所有牺牲了一定的写入数据的效率,都用内存来实现数据的读写的存储

主要步骤:

  • 请求时现将数据保存到预写日志,然后写入内存
  • 在内存中对数据进行排序
  • 将内存中的数据写入文件,构成一个有序数据文件
  • 合并:将有序的多个数据文件进行合并生成一个新的有序的文件
  • 删除和更新:在模型设计中,本身并没有删除和更新,通过插入来模拟的,一般都是在文件合并时来进行实际的物理删除操作
WAL
  • 概念:预写日志, write-ahead-log
  • 职责:当产生写的请求后,写请求提交给regionserver,regionserver会将这个写的操作记录在WAL中,即Hlog,然后再写入memstore
  • 存储:将WAL存储在hdfs上,每台regionserver维护一份WAL,
  • 场景:用于数据恢复,hfile由hdfs恢复,memstore由WAL恢复

补充:

  • oldwals:不再被需要的Hlog日志文件
Flush
  • 概念:刷写
  • 功能:数据在memstore中先做一次按rowkey的字典顺序排序,然后将memstore中的数据写入持久化到hdfs上,会生成一个有序的storefile文件
  • 场景:由于memstore是使用的内存,内存容量有限,为了提高新数据的命中率,需要将老的数据吸入hdfs进行安全存储
Compaction

概念:合并

功能:将hdfs上的多个storefile文件进行合并,构建统一的有序文件

场景:为提供文件的快速读取,将多个storefile文件合并成一个整体有序的storefile文件,因为读取多个数据源没有读取一个数据源快

过程:

代码语言:javascript复制
minor compaction:将hdfs上早些生成的一些文件进行合并
major compaction:将所有的文件进行合并,合并的过程中会将标记为删除或过期的数据真正的删除
Split
  • 概念:分裂.
  • 功能:若一个region分区的数据过多,会分裂成两个region.
  • 场景:一个分区的数据过多时,就会容易造成服务端引起的热点问题,无法实现分布式高性能,此时由regionserver将这一个分区均分为两个分区,由master重新将两个分区分配到 不同的regionserver中,分配成功后旧的region下线.
数据加载

概念:将数据导入到hbase中,有两种实现方式。

  • 使用put对象

例如:hbase shell,java api,mr程序封装。缺点:数据流经过WAL,然后经过内存,最后再到hdfs上,当导入海量数据是,容易导致region和hdfs的io过高,增加服务端负载,影响其他应用。

  • Bulkload
代码语言:javascript复制
原理:hbase底层存储是hdfs上的hfile文件,然后通过meta表关联数据,所以可以先本地将数据转换为hfile文件,然后上传到hdfs上去,同时补充上meta表数据。

场景:适用于导入大量数据的批量hbase场景,要求稳定性能

缺点:

数据第一次读取时都是在hdfs上的,没有存在memstore中,所以第一次会变慢,但是如果数据量特别大的时候,两种方式最终的数据第一次读取都是在hdfs上的,所以没差别
数据直接从传到hdfs上的,没经过WAL,所以当出现数据丢失,没办法恢复数据,需要重新再转换一次

实现:

应用程序实现:

  1. 负责将普通文件转换成为hfile文件
  2. 负责将转换好的hfile文件加载到hbase表中

hbase自带实现:

1.ImportTSV,是hbase-server.jar包中自带的一个命令,可以实现使用put方式将数据导入hbase表中,也实现了将普通文件转换成一个hfile文件的两种方式

2.completebulkload,上面的importtsv命令将普通文件转换成一个hfile文件,但是并没有加载到hbase表中,completebulkload负责将hfile文件加载到hbase表中

代码语言:javascript复制
<命令>:
     1. yarn jar /export/servers/hbase-1.2.0-cdh5.14.0/lib/hbase-server-1.2.0-cdh5.14.0.jar 
        importtsv  -Dimporttsv.columns=a,b,c <tablename> <inputdir> -Dimporttsv.separator=',' 
         -Dimporttsv.bulk.output=/bulkload/output2
     2. yarn jar export/servers/hbase-1.2.0-cdh5.14.0/lib/hbase-server-1.2.0-cdh5.14.0.jar 
          completebulkload /bulkload/output2  mrhbase

<解释>:
    -Dimporttsv.columns 指定文件中列映射的列簇及列         
        Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age,info:sex mrhbase /bulkload/input
    -Dimporttsv.separator 指定读取文件中的列分隔符,默认以制表符分隔
    -Dimporttsv.bulk.output 指定生成的HFILE文件所在的hdfs的位置
SQL on Hbase

背景:

hbase是nosql数据库,有自己的api实现,不支持sql语言,不利于开发和数据分析人员,sql on hbase 解决了这一场景,在hbase上使用sql/jdbc操作

原理:

  • 基于java api/mapreduce 实现
  • 基于hbase shell 实现
  • 搭桥通过hdfs实现

实现:

hive集成hbase:

原理:通过hive中的hql语句,底层转换为mapreduce操作,在mapreduce操作的同时,也用mapreduce操作hbase表 实现:在hive中创建一张与hbase关联的表,操作hive中关联表,实际上是对hbase在操作

phoenix第三方工具:

原理:基于hbase构建了二级索引,直接调用hbase的api实现,因此在于hbase集成度和性能是最优选

sqoop第三方工具:

原理:底层也是使用mapreduce程序导入数据,从关系型数据库中导入到hdfs,然后使用importtsv命令和completebulkload命令来完成从hdfs上的导入,sqoop可以导入,但是不能导出,因为半结构化数据支持结构化数据存储,结构化数据不支持半结构化数据存储

补充:

  • 若hbase表已经存在,hive中只能创建外部表,使用key来表示rowkey
  • 若hbase表不存在,默认以hive表的第一列作为hbase的rowkey
  • hbase与hive关联,hive中的关联表加载数据时不能使用load加载,因为load命令底层没有使用mapreduce,因为load命令是使用hdfs的put命令,只能用insert命令
二级索引

概念:基于rowkey再构建一个索引,称为二级索引。

意义:rowkey是唯一索引,而且rowkey是前缀匹配,若我们不知道前缀,但知道rowkey部分字段,只能全部扫描吗?二级索引就是解决了rowkey唯一索引这个问题。

解决:构建二级索引。

方式:

代码语言:javascript复制
创建索引表,将原表中的查询条件作为索引表的rowkey,将原表中的rowkey作为索引表中的value;
查询是若不指定原表的前缀,就先根据查询条件去查询索引表,找到原表的rowkey,再根据获得的rowkey去查原表
协处理器
  • 背景:构建二级索引,因为索引表和原表是两张不同的表,如何保证两张表的数据同步?
  • 同步方式:手动进行两次插入,在插入原表的同时,也插入索引表
  • 缺点:性能较差,操作繁琐,请求数加倍,对服务器负载过高
  • 构建协处理器,构建类似于mysql中的触发器
  • 依靠第三方工具,让他们来实现二级索引:例如:solr,ES 构建索引类型丰富,可以实现自动同步,Phoenix用sql构建索引
  • 概念:Coprocessor,协助开发者构建自定义的二级索引
  • 本质:自定义实现hbase提供对应接口的一段程序

分类:

  1. observer:观察者的协处理器,类似于监听功能,类似于触发器,一般用于二级索引同步 功能:监听原表,只看客户端往原表中插入一条数据,协处理器自动往索引表中插入一条数据
  2. endpoint:终端者协处理器,类似于存储过程,或java中达到一个方法,一般用来做分布式聚合统计 功能:监听一张表,这张表的每个region都会计算自己的rowkey个数,当客户端调用时,就会返回每个region的个数
  3. 补充:如何快速统计一张表的rowkey个数
  4. 通过编写mapreduce方法,最慢的最蠢的方式
  5. 使用hbase自带的count命令
  6. 开发endpoint协处理,最快最有效的方式
Hbase优化

概念:hbase优化可通过以下几个方面,flush,compaction,split和列簇属性来实现

Flush
  • 意义:用于将memstore中的数据写入HDFS,变成storefile文件,空出内存,用于存储新数据
  • 自动触发规则:
代码语言:javascript复制
默认单个memstore的存储达到128M,就会触发。

默认整个region的memstore的数据达到128M * 4,就会触发。

默认整个regionserver中的memstore的使用率达到堆内存的40%,就会触发。

缺点:

自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发

手动触发:

关闭自动触发:将所有自动触发的条件调高,定期的在没有达到自动触发的条件之前通过flush命令手动触发

总结:

将自动触发条件调高,以此来关闭自动触发flush,并在业务量较少时通过手动触发flush,以此来达到优化目的

Compaction
  • 意义:用于实现将多个storefile文件,合并成一个storefile文件,变成一个整体有序的文件,加快读的效率

自动触发规则:

代码语言:javascript复制
minor compaction:轻量级的合并,合并部分storefile成一个文件,smart算法,将最早生成的三个文件合并,不定期执行,占用IO比较小
major compaction:重量级的合并,合并所有的storefile文件成一个文件
默认七天会触发一次compaction

缺点:

总结:

关闭自动触发major compaction,并在业务量较少时通过手动触发major_compact

Split

意义:将一个region分裂成两个region,分摊region的负载

自动触发规则:

缺点:

代码语言:javascript复制
自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发
手动触发:
关闭自动触发:hbase.client.keyvalue.maxsize=100GB,不可能达到这个值,根据集群region的数据量,来定期手动触发
  • 总结:

关闭自动触发,并在业务量较少时通过手动触发split

列簇属性
  • BLOOMFILTER:布隆过滤器
代码语言:javascript复制
用于在检索storefile文件时,根据索引判断该storefile文件中是否有想要的数据,若没有直接跳过。

级别:none 不开启。

row 判断是否有需要的rowkey,没有就跳过。

rowcol 判断是否有需要的rowkey和cf:c,没有就跳过。
  • Versions:MaxVersions,最大版本数,该列族中的列最多允许存储多少个版本。
  • TTL:版本存活时间,从该版本的timestamp进行计算,一旦到达这个时间,就表示该数据过期,用户hbase自动清理数据。
  • MIN_VERSIONS:最小版本数,该列族中的数据当达到TTL时间以后,最小保留几个版本。
  • BLOCKCACHE:读缓存,是否将这个列簇的数据读到memcache缓存中,默认开启,建议将不经常读取的列簇关闭。
  • IN_MEMORY:代表BLOCKCACHE中的一种缓存的最高级别,默认是关闭的,IN_MEMORY是最高级别的缓存,缓存会定期的进行LRU清理,缓存级别越低,优先会被清理,不要随便开启IN_MEMORY,因为meta表就在IN_MEMORY级别的缓存中
  • BLOCKSIZE:storefile文件块的大小,默认64k,数据块越小,数据块越多,维护索引越多,内存占用越多,读快写慢,数据块越大,数据块越少,维护索引越少,内存占用越少,读慢写快
  • COMPRESSION:Hbase中的压缩其实就是hadoop的压缩,本身就是压缩storefile的存储
Hbase和RDBMS对比

Hbase

代码语言:javascript复制
支持向外扩展
使用API 和MapReduce 来访问HBase 表数据
面向列,即每一列都是一个连续的单元
数据总量不依赖具体某台机器,而取决于机器数量
HBase 不支持ACID(Atomicity、Consistency、Isolation、Durability)
适合结构化数据和非结构化数据
一般都是分布式的
HBase 不支持事务
不支持Join

rdbms

代码语言:javascript复制
支持向上扩展
使用SQL 查询
面向行,即每一行都是一个连续单元
数据总量依赖于服务器配置
具有ACID 支持
适合结构化数据
传统关系型数据库一般都是中心化的
支持事务
支持Join
Hbase的增删改查
  • 功能:hbase中实际没有更新和删除操作,hbase中只有增加和查询操作;更新是将新数据插入内存,,删除是将数据逻辑删除,等后续合并时再做覆盖旧版本数据和物理删除数据
  • 意义:为了快速返回处理读写请求,数据只要写入就里面返回结果
  • get与scan的区别:
代码语言:javascript复制
get命令是hbase中最快的查询方式,因为指定了索引rowkey,但是只能查询一条数据。
scan命令可以返回多条数据,一般结合过滤器使用。
put和delete没有返回结果。
hbase/hive/hdfs/rdbms的区别

hbase

  • 概念:实时读写的数据库
  • 用途:存储数据
  • 场景:用于实现实现大量数据的实时读写文件,例如搜素引擎,一般用于高性能计算架构中,能存储半结构或结构化数据

hive

  • 概念:结构化数据仓库
  • 用途:存储数据
  • 场景:只能用于存储结构化数据,用来构建数据仓库的工具

hdfs

  • 概念:文件存储系统
  • 用途:存储数据
  • 场景:可以用于存储结构化,半结构化,非结构化的数据,适用于一次存入多次读取,例如网盘,用于做数据归档

rdbms

  • 概念:关系型数据库
  • 用途:存储数据

0 人点赞