HBase 中 Memstore-Local Allocation Buffer

在0.90 版本后的 HBase,引入了一个高级机制用于缓解堆内存碎片的问题。此内存碎片问题的产生的主要原因是由于 memstore 上的扰动(频繁的分配与释放内存空间)导致。对应解决此问题的机制为Memstore-Local Allocation Buffer,简称MSLAB。

在一个memstore 满了后,RegionServer会将它flush到hdfs。这样对于长期存在的Key-Value pair(存在于Old Generation 中),则会在堆内存中产生holes(孔洞)。之后若是需要分配新内存,但是没有足够的连续堆内存空间的话,则会触发Full GC,也就会导致stop-the-world 暂停。继而GC机制会重写整个堆内存,若是使用的默认的 CMS算法,则用的是mark-sweep-compact方式进行。此过程会暂停较长时间,影响系统性能。

减少这部分compacting-collection带来影响的重点是:减少碎片。也就是MSLAB做的事情。它主要的思想是:只允许在堆中分配相同大小的对象。一旦这些对象提升到Old Generation,并且最终被回收后,它们会在堆中留下固定大小的holes。而接下来分配的新对象,由于也是相同大小,则可以直接使用这些holes。也就不会产生promotion errors(或是有关空间不足的报错)。因此也就不会产生这类stop-the-world compacting collection。

MSLAB是许多固定大小的缓冲区,用于存储不同大小的KeyValue实例。当一个缓冲区无法完全承载一个新加入的KeyValue时,则会认为此缓冲区已满,并且创建一个新的固定大小的缓冲区。

MSLAB的功能默认在0.92以及之后的版本中已启用。可以使用hbase.hregion.memstore.mslab.enabled 的配置进行修改。每个分配的固定缓冲大小由hbase.hregion.memstore.mslab.chunksize 决定,默认为2MB。这个参数可以根据KeyValue的大小进行调整,例如若是KeyValue的大小为100KB,则可以适当增加MSLAB的大小,以让一个缓冲区可以存储更多的KeyValue。

在缓冲区中,也有一个可存储大小的上边界。由 hbase.hregion.memstore.mslab.max.allocation 指定,默认为256KB。任何比此值大的KeyValue会被直接存储到JVM堆内存。如果我们存储大量比256KB大的KeyValue,则会更早遇到碎片相关的问题导致的暂停。

当然,使用MSLAB也是有代价的:它们会造成对内存的浪费,因为不可能每个buffer都会完全使用完所有字节。所以一个buffer中没有使用到的内存则会被浪费。所以这里是一个权衡:使用MSLABs 从GC中获益,减少碎片导致的暂停,但是内存的使用率不会很高效。若是不使用MSLABs,则可以获取更高效的内存使用率,但是需要处理GC暂停造成的问题。

不过需要注意的是:GC 算法的实现现在已经越来越好,所以在某些算法中,禁用MSLABs后带来的益处可能更大。因为这些算法可以高效并行地维护堆内存以及碎片整理。特别是G1 GC中,已经有用户表示:若是使用G1 GC,则可以关闭MSLABs 功能,而不会导致任何性功能下降。

最后,由于MSLAB buffers需要额外的byte array 复制操作,所以使用缓冲区会比直接使用KeyValue实例稍慢一些。有些客户会根据实际生产环境中的性能表现,衡量是否要开启MSLAB的功能。不过一般是建议开启此功能,因为它用于解决的是GC的长时间暂停。如果没有特定原因,一般不要关闭此功能。

posted @ 2019-10-22 14:53  ZacksTang  阅读(394)  评论(0编辑  收藏  举报