上一章节:
hbase 源代码分析(6)get 过程 详解
http://blog.csdn.net/chenfenggang/article/details/75042658
put 过程:
1)首先根据获得一个客户端的BufferedMutator;
2)当数据量到一定时,或者关闭table,或者手动提交时才开始提交。
3)提前全会分布获取每个put记录的ServiceName,然后建立一个Map<ServiceName,List<ROW>>。分别并行将记录发往各个regionService
4) regionService 获得信息之后,发给个region。数据put的只是放在内存memstore中,当达到阈值之后写入HFile。
这里将逐步分析这个过程。
入口。
Put put = new Put("rowkey".getBytes();
put.addColumn("family".getBytes(),"qualifier".getBytes(),"value".getBytes());
htable.put(put);
@Override
public void put(final Put put) throws IOException {
getBufferedMutator().mutate(put);
if (autoFlush) {
flushCommits();
}
}
获取缓存器,这里是个单例。一个Htable维持一份BufferedMutator,如果自动刷新就会执行flushCommits();
private void backgroundFlushCommits(boolean synchronous) throws
InterruptedIOException,
...
if (!synchronous) {
异步提交。
ap.submit(tableName, buffer, true, null, false);
- .......
}
//否则可以同步。同步需要等待buffer为空。
if (synchronous || ap.hasError()) {
while (!buffer.isEmpty()) {
ap.submit(tableName, buffer, true, null, false);
}
...
}
// 因为中间可以没有提交成功,需要重新计算这部分占用的空间。
} finally {
for (Mutation mut : buffer) {
long size = mut.heapSize();
currentWriteBufferSize.addAndGet(size);
dequeuedSize -= size;
writeAsyncBuffer.add(mut);
}
}
}
提交之前会将消息分组,就好物流分货一样,发往深圳放一堆。
public <CResult> AsyncRequestFuture submit(ExecutorService pool, TableName tableName,
List<? extends Row> rows, boolean atLeastOne, Batch.Callback<CResult> callback,
boolean needResults) throws InterruptedIOException {
...
Map<ServerName, MultiAction<Row>> actionsByServer =
new HashMap<ServerName, MultiAction<Row>>();
....
. do {
//等待有空线程,如果没有会一直等下去
waitForMaximumCurrentTasks(maxTotalConcurrentTasks - 1, tableName.getNameAsString());
- .....
- RegionLocations locs = connection.locateRegion(
tableName, r.getRow(), true, true, RegionReplicaUtil.DEFAULT_REPLICA_ID);
//这个方法,会将相同
getServerName ,row 放在一起。addAction(loc.getServerName(), regionName, action, actionsByServer, nonceGroup);
} while (retainedActions.isEmpty() && atLeastOne && (locationErrors == null));
if (retainedActions.isEmpty()) return NO_REQS_RESULT;
return submitMultiActions(tableName, retainedActions, nonceGroup, callback, null, needResults,
locationErrors, locationErrorRows, actionsByServer, pool);
}
发送前生成
Runnable runnable =
new SingleServerRequestRunnable(runner.getActions(), numAttempt, server,
都包含各种的需要发送的ROW 已经ServiceName
SingleServerRequestRunnable 实现的runnable ,有执行器执行。跑里面的run方法。在里面创建了callable,和caller
由caller调用call(callable
).代码如下:caller很多地方将到来,所以不再详细说明。
@Override
public void run() {
...//new 一个callable
callable = createCallable(server, tableName, multiAction);
..//caller
RpcRetryingCaller<MultiResponse> caller = createCaller(callable, rpcTimeout);
... //调用
res = caller.callWithoutRetries(callable, operationTimeout);
...
- // 检查返回结果
receiveMultiAction(multiAction, server, res, numAttempt);
...
}
在callable的call方法里有个
responseProto = getStub().multi(controller, requestProto);
这里就是发送到RegionService。通过clientprotos 。
下面是服务端。
@Override
public MultiResponse multi(final RpcController rpcc, final MultiRequest request)
throws ServiceException {
...
这里主要做些解析过程,已经认证。
for (RegionAction regionAction : request.getRegionActionList()) {
region = getRegion(regionAction.getRegion());
按region又分一次
ClientProtos.RegionLoadStats stats = mutateRows(region, regionAction.getActionList(),
cellScanner);
}
}
里面再建立批量RowMutations,而且不用区分delete和put
然后
@Override
public void processRowsWithLocks(RowProcessor<?,?> processor, long timeout,
long nonceGroup, long nonce) throws IOException {
- //主要是处理协处理钩子
- startRegionOperation();
//行锁。
Collection<byte[]> rowsToLock = processor.getRowsToLock();
//写
WALKeydoProcessRowWithTimeout(
processor, now, this, mutations, walEdit, timeout);
- //多版本
- // 7. Start mvcc transaction
writeEntry = walKey.getWriteEntry();
mvccNum = walKey.getSequenceId();
- //写入内存
- // 8. Apply to memstore
for (Mutation m : mutations) {
for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
Store store = getStore(cell);
addedSize += store.add(cell);
}
}
}
所以数据put都放在内存中。当
addedSize的尺寸大于阈值时会spit到HFile里面。
这个后面再讲。
到此put过程结束。
未完待续...