HBase上Regionserver的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分做BlockCache,用来读,当然Memstore也有读的功效,不过由于Hbase的scan机制,从Memsotre读到数据的效果一般。
今天主要来分析下Hbase的BlockCache机制,并且阐述其中碰到的一个RTE异常。
话不多说,首先来看看Hbase的存储机制。
其实际存储文件是HFile格式的,这是一个HDFS上的Seq的二进制流
HFILE由以下几部分组成
DATABLOCK DATABLOCK 。。。标志 METABLOCK METABLOCK 。。。DATAINDEX METAINDEX TRAILER
TRAILER为头指针,指向DATAINDEX和METAINDEX,DATAINDEX和METAINDEX分别指向BLOCK的起始位置
DATABLOCK的结构是keyvalue键值对,具体的结构就不分析了
LRU顾名思义就是最近最久未使用方法,查找到内存中最近最久未使用的进行淘汰
RS在启动的时候会启动一个线程专门进行LRU淘汰如下所示:
public void run() {
while(true) {
synchronized(this) {
try {
this.wait();
} catch(InterruptedException e) {}
}
LruBlockCache cache = this.cache.get();
if(cache == null) break;
cache.evict();
}
}
该段代码是一个循环等待LRU进程唤醒条件的发生,当该条件满足时会调用cache的evict()方法进行淘汰。
那么产生的条件是什么呢
整个RS的LRU维护一个HashMap<string,Block>,String 是一个blockpath,由storefilepath+标记+blocknum组成
当有读请求发生时首先会对当前storefile的read对象,synchronized (metaIndex.blockKeys[block])
然后get,若无则加进map中。有则直接返回Block
当加入map以后map的size达到一个阈值,此时:
public void evict() {
synchronized(this) {
this.notify(); // FindBugs NN_NAKED_NOTIFY
}
}
该函数会通知LRU线程进行淘汰。淘汰的规则如下:
将MAP中的数据装入3个桶:In-Memory,muti,single 。3个桶都有容量,且各自维护一个list用来存放放入其中的Block
删除的是批量删除的形式,即如果Map的size大于阈值A,那么此时会释放到一个MAP的初始值大小。
首先选择将要删除的桶,超过桶容量最大的优先选择删除算法如下所示
while((bucket = bucketQueue.poll()) != null) {
long overflow = bucket.overflow();
if(overflow > 0) {
long bucketBytesToFree = Math.min(overflow,
(bytesToFree - bytesFreed) / remainingBuckets);
bytesFreed += bucket.free(bucketBytesToFree);
}
remainingBuckets--;
}
cache可以高效地提升hbase的读TPS,在hbase中具有重要的作用。
Memstore则是写操作的关键,二者在hbase都有重要的作用,当然是越大越好。不过hbase规定二者之和不得超过可用内存的0.8呗,否则会产生不可预知的错误。
下面来看看碰到的一个RTE异常
synchronized (metaIndex.blockKeys[block]) {
metaLoads++;
// Check cache for block. If found return.
if (cache != null) {
ByteBuffer cachedBuf = cache.getBlock(name + "meta" + block,
cacheBlock);
if (cachedBuf != null) {
// Return a distinct 'shallow copy' of the block,
// so pos doesnt get messed by the scanner
cacheHits++;
return cachedBuf.duplicate();
}
// Cache Miss, please load.
}
ByteBuffer buf = decompress(metaIndex.blockOffsets[block],
longToInt(blockSize), metaIndex.blockDataSizes[block], true);
byte [] magic = new byte[METABLOCKMAGIC.length];
buf.get(magic, 0, magic.length);
if (! Arrays.equals(magic, METABLOCKMAGIC)) {
throw new IOException("Meta magic is bad in block " + block);
}
// Create a new ByteBuffer 'shallow copy' to hide the magic header
buf = buf.slice();
readTime += System.currentTimeMillis() - now;
readOps++;
readData +=longToInt(blockSize);
// Cache the block
if(cacheBlock && cache != null) {
cache.cacheBlock(name + "meta" + block, buf.duplicate(), inMemory);
}
return buf;
}
这段代码逻辑性本身是没有问题,但是当出现以下情况时就有触发异常
LRU维护一个HashMap<string,Block>,String 是一个blockpath,由storefilepath+标记+blocknum组成。
当有读请求发生时首先会对当前storefile的read对象,synchronized (metaIndex.blockKeys[block])
然后get,若无则加进map中。有则直接返回Block
但是这个逻辑在Region split和compact的时候会有问题。
假设A split B 和C,此时读取C,那么首先会去get,发现不存在,然后将C的blockpath putin map。
如果compact刚好在get和put之间结束,此时又有读请求过来,那么此时它们的storefilepath是一样的;
由于是getMetaBlock,因此它们的blocknum也一致,故blockpath也一致;
而且compact结束以后创建的是新的reader对象,与C原来的HalfStoreFileReader对象不一致。此时该读请求也会去get map,发现map中没有该blockpath,也会执行put操作。
这样对同一block会有两次put,会抛出RTE: Cached an already cached block 目前来看这个异常不会造成数据丢失,对读操作也无影响,主要发生在getMetaBlock的时候。
由于compact以后对于数据所在block的number进行了更新故在LoadBlock时几乎不发生
目前来看这个异常不会造成数据丢失,对读操作也无影响,主要发生在getMetaBlock的时候。
由于compact以后对于数据所在block的number进行了更新故在LoadBlock时几乎不发生
分享到:
相关推荐
HBase的BlockCache是一种用于提高读取性能的重要机制,它主要用于缓存HFile的Block数据,以减少磁盘I/O操作,从而加快读取速度。BlockCache分为两种类型:LruCache和BucketCache。 - **LruCache**:基于最近最少...
### HBase Bucket Cache:一种高效的缓存管理方案 #### 概述 HBase Bucket Cache 是一个针对 HBase 的块缓存实现,旨在解决 CMS(Concurrent Mark Sweep)垃圾收集器和堆内存碎片带来的性能问题,并提供更大的缓存...
HBase的Block Cache是 RegionServer 中的一块缓存区域,用于存储频繁访问的数据块。增大Block Cache的大小可以提高读取性能。默认情况下,HBase的Block Cache大小为0.0,可以根据实际情况调整这个值。例如,将Block ...
BlockCache 是 HBase 中的一种缓存机制,用于存储经常访问的数据。通过调整 BlockCache 的大小,可以提高 HBase 的读取性能。 3. 读写请求数 读写请求数是一个重要的性能指标,通过观察读写请求数,可以了解每台 ...
- **详细解析**:虽然BlockCache能够显著提高HBase的读取性能,但并不是所有类型的数据块缓存都能够提高效率。例如,如果数据块访问频率很低,将其放入BlockCache中可能并不会带来明显的性能提升。 19. **HBase ...
5. **BlockCache**:配置BlockCache大小,缓存最近访问的数据,提高读取速度。 ### 总结 HBase是大数据处理领域的重要工具,尤其适合实时查询和大规模数据存储。理解并掌握HBase的核心概念、安装配置、数据模型...
例如,`org.apache.hadoop.hbase.regionserver.HStore`类实现了MemStore和BlockCache,它们分别缓存内存中的新写入数据和硬盘上的热数据,提高读写效率。同时,HBase还支持Compaction操作,通过`org.apache.hadoop....
11. **优化策略**:包括合理设置Region大小、预分区表、选择合适的Column Family、启用BlockCache等,以提升HBase的性能。 12. **安全配置**:在生产环境中,可能需要配置HBase与Kerberos进行集成,以实现身份验证...
此外,HBase的内存管理还包括了BlockCache与Memstore的大小比例配置,一般默认BlockCache为20%,Memstore为40%。在读密集型应用中,可以增大BlockCache,减小Memstore,以优化读取性能。BlockCache的三个优先级队列...
`hbase.blockcache.size`配置全局BlockCache的大小,它用于缓存数据块以提高读取性能。`hbase.hregion.memstore.block.multiplier`控制内存中memstore的大小,防止过多数据堆积导致RegionServer崩溃。 10. **性能...
create 'ImagesTable', 'Images', {NAME => 'ImageData', VERSIONS => 1, BLOCKCACHE => true, COMPRESSION => 'NONE', MOB_ENABLED => true, MOB_THRESHOLD => 1048576} ``` 3. 上传文件:使用HBase的Java API...
5. **文件系统和缓存设置**:`fs.defaultFS`设定默认的HDFS文件系统,`hbase.hregion.blockmultiplier`控制BlockCache的大小,`hbase.hregion.memstore.block.multiplier`则用于控制MemStore的大小。 6. **客户端...
- 观察与确认:分析缓存未命中率和配置,同时关注GC日志,评估BlockCache配置的有效性。 - 优化建议:若内存小于20GB,选择LRUBlockCache;否则,考虑使用BucketCache的offheap模式,以减少GC影响。HBase 2.0的...
例如,它改进了BlockCache的管理,提高了缓存效率;增强了MemStore的压缩算法,降低了存储成本;同时,还优化了Region分裂过程,减少了对系统的影响。 2. **多版本支持**:HBase允许用户保留多个版本的数据,便于...
在HBase中,MemStore和BlockCache是两个关键的缓存机制。MemStore用于列族/Store级别的写入缓存,而BlockCache则服务于RegionServer级别的读取缓存。Rowkey长度的控制尤为重要,过长的Rowkey会导致缓存中数据密度...
6. **缓存和预读配置**:`hbase.hregion.blockcache.size`设置了BlockCache的大小,提高读取性能。`hbase.client.scanner.caching`控制每次从RegionServer获取的行数,合理设置可以减少网络传输。 7. **负载均衡**...
- BlockCache: 缓存热数据,加速读取速度。 - Region Splitting & Balancing: 自动分裂大Region和负载均衡,保持集群健康运行。 6. **HBase MPI** - MPI(Message Passing Interface)通常用于高性能计算,将...
5. **性能优化**:分享在项目实践中遇到的问题及解决方案,如Region大小调整、Compaction策略、BlockCache的使用等,以提高Hbase的读写性能。 6. **案例分析**:通过实际项目案例,展示Hbase在互联网、物联网、日志...
3. **实时查询**:HBase支持实时读写操作,通过内存缓存和BlockCache机制,提供了亚秒级的查询响应速度。 4. **稀疏性**:HBase可以存储大量的稀疏数据,即许多行或列可能存在大量缺失值,只存储有实际数据的位置,...