读写过程

读请求过程:

  1. 客户端通过zookeeper以及root表和meta表找到目标数据所在的regionserver
  2. 联系regionserver查询目标数据
  3. regionserver定位到目标数据所在的region,发出查询请求
  4. region先在memstore中查找,命中则返回
  5. 如果在memstore中找不到,去查找blockcache,则在storefile中扫描(布隆过滤器)(可能会扫描到很多的storefile——bloomfilter

布隆过滤器参数类型有2种:Row, Column+Row
行键 和 行键+列

一个完整行的数据可能存放在多个HFile里.
为了读出完整行,HBase可能需要读取包含该行信息的所有HFile

写请求过程:

  1. client向region server提交写请求
  2. region server找到目标region
  3. region检查数据是否与schema(表描述信息)一致
  4. 如果客户端没有指定版本,则获取当前系统时间作为数据版本
  5. 将更新写入WAL log
  6. 将更新写入Memstore
  7. 判断Memstore的是否需要flush为Store文件。

    细节描述

    hbase使用MemStore和StoreFile存储对表的更新。
    数据在更新时首先写入Log(WAL log)和内存(MemStore)中,
    MemStore中的数据是排序的,
    当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。
    于此同时,系统会在zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了。
    当系统出现意外时,可能导致内存(MemStore)中的数据丢失,此时使用Log(WAL log)来恢复checkpoint之后的数据。

StoreFile是只读的,一旦创建后就不可以再修改。因此Hbase的更新其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并(minor_compact, major_compact),将对同一个key的修改合并到一起,形成一个大的StoreFile,当StoreFile的大小达到一定阈值后,又会对 StoreFile进行split,等分为两个StoreFile

由于对表的更新是不断追加的,compact时,需要访问Store中全部的 StoreFile和MemStore,将他们按row key进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内存中索引,合并的过程还是比较快

MVCC

为了理解hbase并发控制,我们首先需要理解为什么hbase需要并发控制,换句话说,hbase的什么属性需要引入并发控制。hbase是基于row级别的ACID事务性,ACID是指:

  1. Atomicity:事务的所有操作要么全部成功要不全部失败;
  2. Consistency:仅有有效的数据才能写入数据库;
  3. Isolation:多个事务执行的时候相互之间不会产生影响彼此的执行;
  4. Durability:一旦事务被提交,数据便被持久化了

如果你有使用关系数据库的经验,这些术语您应该比较熟悉。传统数据库对于数据库中所有数据提供了ACID语义。基于性能考量,hbase仅仅提供行级别的ACID。如果您不是很熟悉这些术语,不用担心。下面通过例子解释下:
write和write-write同步
考量两个篇幅写入hbase的操作,涉及两个列{company,role}
image.png
从之前的一篇介绍hbase写路径的文章中,我们知道hbase将执行以下两步操作,在写的时候:
1. 写入WAL;
2.更新memstore,将每个数据cell写入
现在我们假设没有并发控制的情况下将可能发生以下的执行顺序:
image.png
最后,我们可能得到以下的状态:
image.png
这样却违反了隔离性的要求,所以的确是需要进行并发控制的。最简单的解决方案是在每行上面加锁。这样步骤变为下面:
0 获得行锁;
1 写WAL;
2 更新memstore;
3 释放行锁

read-write同步
目前为止,我们已经为写引入行锁保证ACID。而在读的时候是否也需要呢,下面让我们考虑一下的情况:
image.png
假设我们没有进行并发控制读,这时正好也发生两次写。读如果正好开始于”Waiter”被写入memstore,这个读操作将读到这样的数据:
image.png
因此我们需要并发控制处理read-write同步。最简单的方式是用read也去获取row锁。但是这个却会引起性能的急剧下降。实际上hbase引入了Multiversion Concurrency Control(MVCC)避免read获取锁的的方案。MVCC以下方式:

对于写:
w1 获取到行锁后,每个写操作立刻被分配一个write number;
w2 每个数据cell存储自己的write number;
w3 写操作完成以后要申明其已经完成了

对于读
r1 每个写操作首先被分配一个读时间戳,叫做read point;
r2 每个read point被分配一个最大的大于所有已完成write number的整数;
r3 对于read r将会返回数据cell,其满足所有的write number是小于或者等于read point r的最大值
image.png
每个写呗分配一个write number(step w1),每个数据cell附带一个write number写入memstore(step w2, e.g. “Cloudera[wn=1]”),每个写完成通过完成且write number(step w3)

现在我们考虑下图4的读场景, read开始于step”Restaurant [wn=2]”之后,但在step“Waiter[wn=2]”之前。根据规则r1和r2,read point被分配为1,根据r3,它将会读取write number1的数据:
image.png
现在我们将所有整合下,具体步骤:
0 获取row lock;
0a 获取新的write number;
1 写入wal;
2 更新memstore
2a完成write number
3释放row lock

[

](https://blog.csdn.net/huanggang028/article/details/46047927)