HBase读写流程底层原理
上一篇我们已经熟悉了RegionServer所有的组件了,我们今天了解一下HBase的写流程,我们需要将之前的组件串联起来
写流程
我们在写的时候会有一个Put操作,图中我们有一个Client客户端,这个客户端可以是我们的Shell客户端也可以是我们的API客户端,API客户端指的是我们写的JAVA代码,里面还有一个zk,但是没有Master。
那我们写数据的时候,我们是怎么写的呢?我们今天想写数据肯定要一个put请求。
关键问题来了,我这个put应该放在哪个Region中去,我想判断,是不是应该根据表中的Region信息去判断呢?每个Region都有自己的一个RowKey范围,一个Region从多少到多少,另一个Region从多少到多少,我只要拿到这个RowKey的范围,我就应该能知道我要写到哪个Region中去,关键就是获取这个RowKey的范围。这个应该怎么获取呢?
在HBase中有一张属于自己的系统表叫做meta,meta是元数据的意思,这个元数据指的就是所有表的Region的元数据,这张mate表存储的就是HBase所有表的Region的信息,
里面信息是什么样的呢?我们看图说
每个Region在meta表里都有自己的信息。
这个Region里面都会有什么呢?有起始的K和结束的K,startRow和stopRow,起始的RowKey范围。还有什么呢?还有当前这个Region被哪个RegionServer管理。也就是说,Region所属的RegionServer,也是信息的一部分,也就是说,我们的客户端拿到这条元数据信息,我们就知道数据应该写往哪个Region,推出来Region了,我们不久知道应该找哪个RegionServer了么,我们的写流程慢慢的清晰了,我们写的时候,去元数据中判断,这个数据应该属于哪个Region,根据是它有RowKey范围,我们根据RowKey范围去找到Region,然后去找到对应的RegionServer,我们读写数据都是由RegionServer去负责的。找到之后我们就可以请求RegionServer了,然后RegionServer就可以给我们去写数据了,关键问题来了,如何拿到元数据信息,元数据信息存储在meta表中了,想拿表,是不是涉及到从HBase中读一张表出来,当正常表去读,但是元数据表只有一个Region,问题又来了,读数据怎么读?读数据是不是也是RegionServer去读?那么元数据的Region位于哪个RegionServer呢?我想拿表,要请求哪个RegionServer?zk会告诉我们元数据的Region在哪里,在zk下面有一个hbase/meta - region - server节点,这个节点它会记录元数据表,它的Region位于哪个RegionServer,我们自己也可以去看一下的,这里不带大家去看,自己看。zk是元数据的一个入口,我们读写数据都要请求zk,我们第一步应该请求meta表所在的RegionServer。
这时候zk会返回给客户端告诉他元数据meta表在集群的哪个节点。
这个时候我的客户端就会请求它给的节点。告诉它我要读取meta表
这个时候我们的服务器节点会将meta表信息里面的数据返回给客户端。
客户端相当于拿到这个表了,但是客户端会频繁的读写这个数据,那么客户端会将元数据这个表缓存下来,这样读写速度会更快。在同一个绘画下面我客户端进行读写的话,不用每次请求zk了,直接找缓存拿。只有客户端第一次请求写的时候,才会请求zk,其他的直接找缓存。
我们考虑一个问题,元数据在缓存中的这个信息会不会变?是有可能会变的,我们这个元数据是指的所有的Region信息,region它自己大了会分裂的,所以它是会变的。它如果发现缓存中的数据位置和实际我们找的对不上,说白了找不到,他会报错的,然后它会再次想zk发送请求,重新获取元数据信息,再将它放到缓存中。
拿到元数据之后,我们就会知道去哪个RegionServer去写数据了,就会向对应的节点发送这个写请求。
收到请求之后先写哪里?要写WAL中。预写日志。
写完之后往哪里写?往MemStore里面写。
其实我们将数据安全的写到MemStore中就相当于写完了。不需要数据写道文件当中再返回。这个时候会给客户端发送一个ACK,告诉它,我写完了。
总结:
- Client先访问zookeeper,获取hbase:meta表位于哪个Region Server。
- 访问对应的Region Server,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端?m?eta cache,方便下次访问
- 与目标Region Server进行通讯;
- 将数据顺序写入(追加)到WAL;
- 将数据写入对应的MemStore,数据会在MemStore进行排序
- 向客户端发送ack
- 等达到MemStore的刷写时机后,将数据刷写到HFile
MemStore Flush
我们梳理一下写请求,当走到RegionServer的时候,我们会写入WAL然后再将数据写入道MemStore,这个时候达到一定的阈值我们会进行Flash。拿我们细细的掰扯一把Flash。三里屯音乐走起。
言外之意,什么时候Flash?这个是大家比较关心的。MemStore Flush就是我们一直提到的那个刷写,我们假设又一个Region,Region中又两个Store,这两个Store怎么来的?是因为我们表中又两个列族,每个Store中又一个MemStore。我们看一下它是怎么刷写的。
Flush!!!我们会生成两个文件,刷的时候怎么刷的,一个MemStore刷一个文件,刷的时候也是一个Region中的两个Store一起刷的。
然后我们再刷,还是一起刷,每个MenStore都生成自己的新文件。
我们Flash的时候,是以Region为单位进行Flash的。我只要一刷写,就刷写整个Region中所有的MemStore
它这么设计会不会又什么不好的地方呢?小文件会过多。当一个达到了128,另一个还没有128的时候,那么没有达到128的那个是不是就是小文件了呢,我们看图说
我们HDFS上是杜绝小文件的,所以这个时候我们要注意一个点,就是我们在使用HBase的时候,要减少列族的数量。HBase表最少只要一个列族,最多不要超过三个。我们如果只有一个列族的话,我们就只有一个Store,只有一个Store我们自己刷自己就可以了啊,就不会影响到其他了。列族越多,产生小文件越多。
MemStore严格的说有5个刷写时机。
第一个:
以单个的MemStore为触发条件
当某个memstroe的大小达到了hbase.hregion.memstore.flush.size(默认值128M),其所在region的所有memstore都会刷写
注意: 当memstore的大小达到了
hbase.hregion.memstore.flush.size(默认值128M * hbase.hregion.memstore.block.multiplier(默认值4)时,会阻止继续往该memstore写数据,也就是说,当memstore的大小达到了,128乘4=512兆的时候,它就不让写数据了。
什么时候能写?等Flash完之后,才能往里面写数据。但是,不是说128我就Flash了么?怎么扯出个512兆?我们能达到512兆么?其实是有可能的,我们是这样一个操作,我们定时的检查memstore里面的数据,第一次检查的时候,检查是120兆,我们走了,那么这个时候它肯定还在涨数据,超过128是很有可能的,
第二个:
以单个的RegionServer为触发条件。
当region server中memstore的总大小达到
java_heapsize
*hbase.regionserver.global.memstore.size(默认值0.4)
*hbase.regionserver.global.memstore.size.lower.limit(默认值0.95)
region会按照其所有memstore的大小顺序(由大到小)依次进行刷写。直到region server中所有memstore的总大小减小到上述值以下,当region server中memstore的总大小达到java_heapsize
*hbase.regionserver.global.memstore.size(默认值0.4)
时,会阻止继续往所有的memstore写数据。
补充:
我们的RegionServer是一个JAVA进程,java_heapsize指的是regionServer堆内存,hbase.regionserver.global.memstore.size指的是MemStore它占总内存的一个比例大小,比如说我刚开始给regionServer分配了32G堆内存,所有的MemStore加在一起一共占用多大的内存呢?占用0.4。用0.4乘32就能够得出所有MemStore所占的所有的堆内存。也会有0.4分给BlockCache,作为读缓存。当所有的MemStore之和达到了百分之95的时候,这个时候都快满了,所以要刷写了。刷写的时候要对Region进行排序,从大到小排序。它不会一次性的将东西全部刷写,达到阈值之下就可以了。如果都刷写了会产生多个小文件的。它也有阻塞在里面。
第三个:
到达自动刷写的时间,也会触发memstore flush。自动刷新的时间间隔由该属性进行配置hbase.regionserver.optionalcacheflushinterval(默认1小时)
补充: 说白了,没有达到128,我没法Flash,但是我设置个时间,没有达到128的,达到一个小时我也会Flash,因为数据老是放在内存里也是不好的。
第四个:
当WAL文件的数量超过hbase.regionserver.max.logs,region会按照时间顺序依次进行刷写,直到WAL文件数量减小到hbase.regionserver.max.log以下(该属性名已经废弃,现无需手动设置,最大值为32
需要更多大数据资料请加微信