Hbase Put流程
1.客户端提起请求
HTable.put();
|-->writeBuffer.add(put); |刷入内存缓存,可设置实时生效
|-->线程池提交
|-->for (Entry<HRegionLocation, MultiAction<R>> e: actionsByServer.entrySet())
|-->futures.put(e.getKey(), pool.submit(createCallable(e.getKey(), e.getValue(), tableName)));
|-->client调用server.multi(MultiAction<R> multi)
2.RegionServer找到region
|-->Hregionserver.multi()
|-->for (Map.Entry<byte[], List<Action<R>>> e : multi.actions.entrySet()) |Action对象包括delete,put,get等对象,command模式
|-->if (action instanceof Delete || action instanceof Put)
|--> mutations.add(a);
|-->HRegion region = getRegion(regionName);
|-->for (Action<R> a : mutations)
|--> mutationsWithLocks.add(new Pair<Mutation, Integer>(m, lock));
|-->OperationStatus[] codes = region.batchMutate(mutationsWithLocks.toArray(new Pair[]{}));
3.region处理:一个column family对应一个storefile对象,但是flush的时候是整个region下的全部store一起flush
|-->while (lastIndexExclusive < batchOp.operations.length)
|-->Map<byte[], List<KeyValue>> familyMap = mutation.getFamilyMap();
|-->for (int i = firstIndex; i < lastIndexExclusive; i++)
|-->addedSize += applyFamilyMapToMemstore(familyMaps[i], w);
|-->for (Map.Entry<byte[], List<KeyValue>> e : familyMap.entrySet())
|-->byte[] family = e.getKey(); |column family名称
|-->List<KeyValue> edits = e.getValue(); |KeyValue对应的是每一个column的键值对
|-->for (KeyValue kv: edits)
|-->size += store.add(kv); |添加至store当中
|-->this.memstore.add(kv); |实质为column family对应一个storefile对象里的memsotre内存对象
|-->flush = isFlushSize(this.addAndGetGlobalMemstoreSize(addedSize)); |判断是否需要flush内存
-->if (flush)
|-->requestFlush()
4.requestFlush()
|-->this.rsServices.getFlushRequester().requestFlush(this);
|-->MemStoreFlusher.requestFlush(region); |处理当时region
|-->this.regionsInQueue.put(r, fqe);
|-->this.flushQueue.add(fqe);
5.MemStoreFlush()线程持续做flush操作
|--> if (!flushRegion(fre))
|-->flushRegion(region, false)
|-->boolean shouldCompact = region.flushcache()
|-->boolean result = internalFlushcache(status);
|-->internalFlushcache(this.log, -1, status);
|-->for (StoreFlusher flusher : storeFlushers) {
|-->flusher.prepare(); |构造snapshot
|-->for (StoreFlusher flusher : storeFlushers) {
|-->flusher.flushCache(status); |刷新工作在此进行,完成实质的flush过程
|--> Store.this.flushCache()
|--> internalFlushCache() |构建memstoreScanner,然后逐步写入hfile当中
|-->for (StoreFlusher flusher : storeFlushers) {
|-->boolean needsCompaction = flusher.commit(status);
|-->if (needsCompaction) {
|-->compactionRequested = true
6.HFile文件写hdfs的结构:以块为单位,HFileBlock
|-->append(key,value)
|-->checkBlockBoundary() |实际的写HDFS操作,条件为满64k
|-->finishBlock()
|-->fsBlockWriter.writeHeaderAndData(outputStream); |outputStream的路径为file定义的path路径
|-->实质为写入byteArray的数据流
|-->DataOutputStream out = fsBlockWriter.getUserDataStream();
|-->out.put(key)
|-->out.put(value)
|-->finishBlock() |HFileBlock中进行flush()
|-->userDataStream.flush(); |实质为字节流
|-->doCompressionAndChecksumming() |压缩字节流
7.备注:无论是split还是compact都是通过compactSplitThread 启动的
flushCache完成memstore转向hfile文件
7.1 compact目前是针对单个的store也就是column family,下述方法实现compact合并
StoreFile.Writer writer = compactStore(filesToCompact, cr.isMajor(), maxId);
compact流程
|-->server.compactSplitThread.requestCompaction(region, getName());
|-->requestCompaction(r, s, why, Store.NO_PRIORITY);
|-->CompactionRequest cr = s.requestCompaction(priority);
|-->boolean isMajor = (filesToCompact.getFilesToCompact().size() == this.storefiles.size()); |如果StoreFile下所有文件参与,则与majorcompaction,否则为minor compaction
|-->ret = new CompactionRequest(region, this, filesToCompact, isMajor, pri);
|-->ThreadPoolExecutor pool = s.throttleCompaction(cr.getSize())? largeCompactions : smallCompactions; |根据CompactionRequest的大小确定使用大小compaction线程池执行compact操作
注意:先根据是否全部storefiles全部compact,如是为major compact,与minor compact区别在于是否会执行delete
然后再根据CompactRequest的大小来确定使用large/small 线程程进行处理,线程数不同
7.2 Split操作
a:获取midkey
b:生成splittrasaction
c:创建并生效二个子Region
d:注册至zookeeper
HFile读写见Hbase4test工程
HFile结构见http://blog.163.com/liaoxiangui@126/blog/static/79569640201212311415217/