Hbase

1. Hbase基本概念

一. 为什么要用hbase

  1. 相比于HDFS
  • HDFS适合批处理场景, 但不支持数据的随机查找
  • HDFS不支持数据的更新
  1. 相比于Hive
  • Hive适合批处理的数据分析场景
  • Hive不适合实时的数据访问
  1. Hbase的特点
    顺序写, 随机度, 可擦写, 可删除

二. Hbase概念模型特点

  1. Hbase vs Mysql
      • Hbase的列可以无限扩充
      • Hbase的列没有类型
      • Hbase的多个列构成一个列簇(column family)
    1. 主键 vs 索引
      • Hbase仅有一个主键
      • Hbase存储的每条记录根据主键进行排序
      • Hbase的值是多版本的, 每个版本的值称为Cell
      • Cell的数目(每个值的版本数)也没有限制
  2. Hbase模型特点
    1. 可扩展性强
      • 支持数十亿行, 数百万列
      • 支持数十万个版本
    2. 可以存储很稀疏的数据
      • 即使有90%的列是空值, Hbase也不会因此浪费存储空间
    3. 支持随机读
      • 根据主键会哦去一行数据
    4. 支持scan
      • 快速获取某些行区间内的数据
      • 快速获取某几列的数据
  3. 行健举例
    历史账单查询: userid+time+id
    网络爬虫: URL+爬去实践+优先级

三. Hbase物理模型

  1. 行式存储 vs 列式存储
    1. 行式数据库
      • 没建索引的行式数据库, 查询会产生大量IO
      • 建立索引和物理视图会话费大量时间和资源
      • 若要提升查询性能, 数据库必须被大量膨胀
    2. 列式数据库
      • 数据按列单独存放
      • 数据即是索引
      • 若只查询指定的几列, 可以大量降低IO
      • 每一列可由一个thread单独处理-查询的并发
      • 因为相同列的数据在一起存储, 数据类型一致, 特征相似, 方便高效压缩存储
  2. Hbase的列簇式存储
    • Hbase没有选择纯粹的列式存储, 因为Hbase支持上百万个列, 每个列单独形成文件的话会造成每个表的metadata信息多大, 而且会导致产生数百万个小文件, 降低查询效率
    • 采用列簇式存储: 同一个列簇的所有列的数据存放在一起; 不同列簇的数据在物理上分开
  3. 列簇是排好序的Cell构成的
    1. Cell的构成
      • row key
      • column family
      • qualifier(列)
      • timestamp
      • value
        (其中,前4个构成key)
    2. Cell排序规则
      • row key: 升序
      • column family: 升序
      • qualifier: 升序
      • timestamp: 降序
    3. Cell支持二分查找

四. Hbase划分子表

  1. Region模型
    • Hbase把一张大表拆分成多个子表, 每个子表成为一个Region
    • 不同Region放在不同机器上
    • 子表按照行健范围划分.
      不存在一个row key的数据被划分到2个不同region的情况
    • 每个Region都包含所有列表
  2. Region的存储思路
    • Region内部包含多个列簇, 每个列簇被一个Store存储
    • 每个Store把数据分为两部分: MemStore和StoreFile
    • MemStore只存储新增的和修改过的数据, 并在内存满的时候将数据刷新到StoreFile

五. Hbase的特点

  1. Hbase应用场景
    1. 用户画像
      • 用户画像中用户的特征有几十万个, 但大部分特征都是空的, 是一个稀疏的矩阵
      • mysql存储上亿行, 几十万列的情况会造成巨大浪费
    2. 网页搜索倒排索引
    3. 淘宝交易数据
  2. hbase是强一致性的
  3. 如果需要更新数据, 或进行随机读写, Hbase比hdfs更合适
  4. Hbase的字段部分数据类型, 全都按照字节存储, 需要上次应用系统按照类型翻译字节
  5. Hbase只有聚簇索引(主键索引),没有二级索引:
    • 聚簇索引: 把这个字段抽取出来, 在这个字段上遍历查找, 而不用去全表扫描, 减小磁盘IO
    • 二级索引: 把索引字段排序后进行索引
  6. null记录, 在Hbase中不占空间
  7. Hbase不是列式存储,而是列族式存储
    • 把经常需要一起读写的业务字段设置成一个列族

2. 架构

一. 过滤器

  1. Filter简介
    • hbase为scan和get操作提供更加高级的filter
    • Filter可以根据qualifier,列簇,版本等条件对行进行过滤.减少返回的结果数量, 降低网络传输压力
    • 过滤操作在RegionServer上执行
  2. SingleColumnValueFilter: 实现列簇/列上的where语义
  3. RowFilter, 实现row key的正则表达式过滤
  4. FamilyFilter,
  5. QualifierFilter,
  6. FilterList, 实现查询条件and语法
    各种过滤器
    布隆过滤器
    Hbase架构

二. Compact原理

  1. Hbase基于Log-Structed Merge Tree架构的
  2. 传统的树能够执行快速随机读取, 但随机写入因为要调整树形结构, 因此在大数据量下会变的很慢
    • 为什么树的随机读取很快?
      树是多路查找树, 所以要随机查找一个数值, 将节点调入内存的次数只和树的高度有关. 又因为树的每个节点包含很多值, 所以节点调入内存时产生的磁盘IO是顺序IO, 又加快了节点调入内存的速度
  3. LSM tree如何快速执行随机写?
    • LSM tree首先在内存中维护一个有序的树形结构,类似树,称作MemStore. 随机写入的数据会在MemStore中调整树型后插入这个有序树中.
    • 为了保证数据的可靠性, 在写入Memstore前会先以WAL(Write-Ahead-Log)的方式备份写日志
    • 当MemStore的数据量到达一定条件后, 会被刷新到磁盘上形成一个新文件"HFile". 因为这种写磁盘是顺序写, 没有旧文件被读取修改, 因此执行很快
    • "HFile"在LSM tree架构里又称sstable, 意为相似的有序文件.当执行随机读取时, 首先在MemStore中查找是否存在这个key, 是的话直接返回, 不是的话查找每个HFile中的记录. 因为HFile中的记录和Memstore一样也是有序的树形结构, 所以查找一个HFile的时间复杂度 , 所有的HFile查找时间复杂度 . 因此, 如果HFIle的数量很多, 会使得查找速度变慢, 需要Compact操作
    • LSM tree主要是对写优化
      综上, 因为随机写入是分批量的顺序写入磁盘, 所以速度很快
  4. Bloom过滤器, 减少HFile的筛选
    为了减少随机读取时需要查找的HFile数量, 可使用Bloom过滤器先行判断带查找的key是否在这个HFile里. 如果Bloom过滤器返回False, 则key一定不在HFile里, 如果返回True, 仍有一定概率不在这个HFile里
  5. Compact
    • Minor compact
      将一些小的,相邻的HFile合并成一个大的HFile. 此过程不会处理过期或已删除的kv对
    • Major compact
      把一个Region下的所有HFile合并成一个大的HFile. 此过程会清理掉3类无意义的数据: 被删除的数据, TTL过期数据, 版本号超过限制的数据
      Major compact耗时很长, 通常关闭自动major compact, 改为业务低峰期时手动处理Major Compact

三. Bloom过滤器

  1. 上面的LSM tree架构中提到, 随机读时使用Bloom过滤器减少查找的HFile数量, 在Hbase就是使用Bloom过滤器判断该rowkey是否可能在该HFile中
  2. 算法描述
    • Bloom过滤器用于判断一个元素是否在一个集合内,是一种概率数据结构
      它返回"可能在集合内"和"绝不可能在集合内"2个结果. 集合中的元素越多, Bloom误判成可能在集合中的概率就越大
    • Bloom过滤器定义k个hash函数, 和一个长度为m的bit数组(bitmap,java中用BitSet表示).
      数组被初始化为全0数组, 每个哈希函数会把集合中的元素用射到数组的一个位置, 并把该位置的数组值置1.
      测试一个元素是否在集合中, 用预先定义的k个hash函数依次哈希该值, 得到k个位置, 查看数组中这k个位置的值是否全部是1, 有一个为0则返回"一定不在集合中"
      m的选择决定了Bloom过滤器误判概率的大小
    • k个hash函数应该选择互相之间少有重叠的, 比如"2次方hash,3次方hash"
  3. 概率解释
    • 误判成"可能存在"的概率
      假设hash函数会把一个值等概率的映射到数组的m个位置, 则依次hash后, 该位置的数组没有被置为1的概率为
      k个不相关的hash函数均未把该位置置1的概率为
      插入n个元素后, 该位置仍为0的概率
      即该位置为1的概率
    • k的选择
      k只由误判的概率决定, 有

四. zookeeper中的meta表

  1. meta表的作用

    • 我们知道HBase的表是会分割为多个Region的,不同Region分布到不同RegionServer上。
    • Region 是 HBase中分布式存储和负载均衡的最小单元。
      所以当我们从客户端读取,写入数据的时候,我们就需要知道数据的Rowkey是在哪个Region以及我们需要的Region是在哪个RegionServer上。而这正是HBase Meta表所记录的信息。
  2. meta表也是一张Hbase的表

    • meta表也是一张Hbase表, 有rowkey, 列族, 列
    • hbase的所有查询操作, 都要先访问meta表, 找到自己所查记录的表和rowkey在哪个Region上, 再到那个Region上执行memstore或hfile的查找
  3. meta表的rowkey

    • meta表的rowkey组成结构: table名 + region的start key + 时间戳
    • 上述3者的MD5值也是HBase在HDFS上的region名
  4. meta表的列族和列

    • 最主要的Family:info
    • info里面包含三个列:regioninfo, server, serverstartcode。
      其中regioninfo就是Region的详细信息,包括StartKey, EndKey 以及每个Family的信息等。server存储的就是管理这个Region的RegionServer的地址。所以当Region被拆分、合并或者重新分配的时候,都需要来修改这张表的内容。
  5. Hbase数据查询过程:

    1. 客户端连接zookeeper, 查找meta表在哪个机器上
    2. 查询meta表, 根据每个region的startKeyendKey找到region所在的server
    3. 到指定Server上查找该region
    4. meta标的信息会被缓存起来, 便于下次查找

五. HBase的master-slave架构

  • HMaster
    • 每个HregionServer都与HMaster保持心跳通信.
    • HMaster负责给HRegionServer分配HRegion
    • 当一台HRegionServer宕机, HMaster会把这个RegionServer的Region标记为未分配, 然后再把他们分配到其他HRegionServer中
    • HMaster的功能: HMaster主要负责TableRegion的管理工作
      • 管理用户对Table级别的增删改查
      • 负责HRegionServer的负载均衡, 调整Region在每台机器的分布
      • 在Region分片后, 负责新的Region分配
      • 在HRegion Server停机后, 负责失效的HRegionServer上的Regions迁移
  • HRegionServer
    • 用户通过访问HRegionServer获取这些数据
    • 一台机器上面一般只运行一个HRegionServer
    • 一个HRegionServer上面有多个HRegion,一个HRegion 也只会被
    • 一个HRegionServer维护

3. Rowkey设计

1. Rowkey

唯一标识一行记录的主键, Hbase按照RowKey的字典顺序全局排序

2. 稀疏矩阵

Hbase中表的数据按照稀疏方式存储. 因为不想传统数据库组织数据形式, Hbase的列组成很灵活, 行与行之间不遵循相同的列定义

3. Region

Hbase不同于spark的hash code分区, 使用"key range"划分子表"Region". Region是Hbase负载均衡的基本单元, Region增大到一定大小后会自动分裂

4. column family

Region是对Hbase表的横向切割, 则Column Family是对表的纵向切割. 每个column都要归属于一个Column Family, 这个归属关系是在写数据时指定的而不是建表时预先定义的

5. kv形式存储

Hbase中每行的每一列数据都以key-value形式存储. key相同的任意数量的独立key-value形成逻辑上的一行数据

Hbase行键设计

往往组合多个字段在一起作为rowkey, RowKey中的第一个字段称之为"先导字段"

1. 反转

如果先到字段本身会带来热点问题, 但该字段的尾部却具有良好的随机性; 此时可将先导字段做反转处理

2. 加盐

在rowkey前面加上长度固定的随机Bytes

3. 哈希

将rowkey哈希后生成固定长度的byte串. 由于hash有很好的离散度, 所以可保障数据被均匀分散到各个region. hash rowkey既适合于随机写入, 也适合于知道rowkey后的随机查询, 带在查询一个范围内的rowkey时会有性能问题, 因为原本连续的rowkeyhash后被离散到多个region

列的设计

hbase中的每一列都是key-value形式的. 定义列就是要定义出列簇和列标识符. 列簇和列标识符应该尽量短


一条数据的HBase之旅,简明HBase入门教程-开篇

一. 前言

  1. Region是一个连续范围内的rowkey空间, 连续指Region中的rowkey在start key到end key范围内排序
  2. Region之间不会产生交叉, 一个rowkey只能属于一个Region, 被一个RegionServer管理
  3. meta表中, 使用3层B树找到某个表的某个rowkey所在的Region
  4. 当初次创建一张表时, Hbase只会分配一个Region给这张表. 这意味着初始时, 所有的request都会通往一个RegionServer上

二. PRE-SPLITTING

  1. 为什么使用预先划分?
    上面讲到, 一张表初始创建时, 因为只有一个Region, 所以所有的请求都会发往一个RegionServer, 造成压力巨大. 如果创建表时就制定Region的数量, 并按照一定的规则将rowkey发往不同的Region, 就会在初始就使用集群的分布式功能

  2. 预划分的start key和end key生成策略
    预划分需要计算表的分割点"split points", 有两种默认的预划分算法:

    • HexStringSplit:
      如果rowkey是ASCII码表示的, 可以使通过MD5生成或者其他产生均匀分布的16进制表示法. rowkey范围为"00000000" => "FFFFFFFF"的范围, 左侧补0来使rowkey长度相同. 它把rowkey均匀分配到Region上, 这中做法会造成更大的空间使用量, 且不够直观
    • UniformSplit:
      如果rowkey是字节数组,可以用UniformSplit的方式进行,按照原始byte值(从0x00~0xFF)右边以00填充。以这种方式分区的表在插入的时候需要对rowkey进行一个技巧性的改造, 比如原来的rowkey为rawStr,则需要对其取hashCode,然后进行按照比特位反转后放在最初rowkey串的前面
    Integer.reverse(Integer.valueOf(Integer.valueOf(i).hashCode())))
    
  3. 通常使用预划分建表指定region数量以及region划分算法, 后面使用Region自动划分

三. AUTO SPLITTING

  1. 什么时候执行自动划分
    无论是否开启了pre-spliting, 一旦Region到一定限制, 就会自动化分为2个Region. 有3种预定义的自动划分策略
    • ConstantSizeRegionSplitPolicy
    • IncreasingToUpperBoundRegionSplitPolicy
    • KeyPrefixRegionSplitPolicy.

四. Force-split(手工切分)

你可能感兴趣的:(Hbase)