埃尔法哥哥■深入HBase读写

在了解HBase架构的基础上 , 我们需要进一步学习HBase的读写过程 , 一方面是了解各个组件在整个读写过程中充当的角色 , 另一方面只有了解HBase的真实请求过程 , 才能为后续的正确使用打下初步基础 , 毕竟 , 除了会使用api , 你还得知道怎么能写得更快 , 怎么查得更快 。
1.首次读写的基本过程
在上一篇深入HBase架构(建议收藏)中已经做了介绍 。 这里再重申一下 。
这里要解决的主要问题是 ,
client如何知道去那个regionserver执行自己的读写请求 。
有一个特殊的HBase表 , 叫做METAtable , 保存了集群中各个region的位置 。
而这个表的位置信息是保存在zookeeper中的 。 因此 , 当我们第一次访问HBase集群时 , 会做以下操作:
1)客户端从zk中获取保存metatable的位置信息 , 知道metatable保存在了哪个regionserver , 并在客户端缓存这个位置信息;
2)client会查询这个保存metatable的特定的regionserver , 查询metatable信息 , 在table中获取自己想要访问的rowkey所在的region在哪个regionserver上 。
3)客户端直接访问目标regionserver , 获取对应的row
这里我们需要关注一定 , 在读写的过程中 , 客户端实际上是不需要跟HMaster有任何交互的 。 这也是为什么我们在客户端的配置中 , 连接地址是填写的zookeeper的地址 。
埃尔法哥哥■深入HBase读写
文章图片
metatable信息都可以在client上进行缓存(apache的原生abase-client类的Connection的实现类中) 。
2.写请求
从上文我们知道了 , client如何找到目标regionserver发起请求 。
接下来 , 就是正式的写操作了 。
当client将写请求发送到客户端后 , 会执行以下流程 。
(1)获取行锁:HBase中使用行锁保证对同一行数据的更新都是互斥操作 , 用以保证更新的原子性 。
(2)AppendHLog:顺序写入HLog中 , 并执行sync 。
(3)写缓存memstore
(4)释放行锁
这里需要重点关注WAL 。
WAL(Write-AheadLogging)是一种高效、高可靠的日志机制 。
基本原理就是在数据写入时 , 通过先顺序写入日志 , 然后再写入缓存 , 等到缓存写满之后统一落盘 。
为什么可以提高写入性能和可靠性呢?
众所周知 , 对于磁盘的写入 , 顺序写性能是远高于随机写的 。 因此 , WAL将将一次随机写转化为了一次顺序写加一次内存写 , 提高了性能 。
至于可靠性 , 我们可以看到 , 因为先写日志再写缓存 , 即使发生宕机 , 缓存数据丢失 , 那么我们也可以通过恢复日志还原出丢失的数据 。
另一方面 , 我们需要关注一下HBase中的各个结构的关系 。
埃尔法哥哥■深入HBase读写
文章图片
每个regionserver上只有一个HLog , 但是有多个region 。
每个HRegion里面有多个HStore , 每个HStore会有一个写入缓存memstore , memstore是根据columnfamily来划分 。
因此 , 在一个写入操作中 , 我们对任意一行的改变是落在memstore上 , 然后HBase并不会直接将数据落盘 , 而是先写入缓存 , 等缓存满足一定大小之后再一起落盘,生成新的HFile 。
3.读请求
HBase-client上的读请求分为两种 , Get和Scan 。
Get是一种随机查询的模式 , 根据给定的rowkey返回一行数据 , 虽然Get也支持输入多个rowkey返回多个结果 , 但是本质上是多次随机查询 。 具体rpc次数 , 看查询list的数据分布 , 如果都分布在一个regionserver上 , 就是一次rpc , 如果是分布在3个rs , 就是3次rpc , 但是是并发请求和返回的 , 时间取决于最慢的那个 。
Scan是一种批量查询的模式 , 根据指定的startRow和endRow进行范围扫描 , 获取区间内的数据 。


推荐阅读