hbase 源代码分析 (7) put 过程 详解

来源:互联网 发布:sql 报表开发工具 编辑:程序博客网 时间:2024/06/07 21:54


上一章节: 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。

这里将逐步分析这个过程。
入口。
    1. Put put = new Put("rowkey".getBytes();
  1. put.addColumn("family".getBytes(),"qualifier".getBytes(),"value".getBytes());
  2. htable.put(put);
  1. @Override
  2. public void put(final Put put) throws IOException {
  3. getBufferedMutator().mutate(put);
  4. if (autoFlush) {
  5. flushCommits();
  6. }
  7. }
获取缓存器,这里是个单例。一个Htable维持一份BufferedMutator,如果自动刷新就会执行flushCommits();

  1. private void backgroundFlushCommits(boolean synchronous) throws
  2. InterruptedIOException,
  3. ...
  4. if (!synchronous) {
  5.         异步提交。
  6. ap.submit(tableName, buffer, true, null, false);
    1. .......
  7. }
  8.         //否则可以同步。同步需要等待buffer为空。
  9. if (synchronous || ap.hasError()) {
  10. while (!buffer.isEmpty()) {
  11. ap.submit(tableName, buffer, true, null, false);
  12. }
  13. ...
  14. }
    1. // 因为中间可以没有提交成功,需要重新计算这部分占用的空间。  
  15. } finally {
  16. for (Mutation mut : buffer) {
  17. long size = mut.heapSize();
  18. currentWriteBufferSize.addAndGet(size);
  19. dequeuedSize -= size;
  20. writeAsyncBuffer.add(mut);
  21. }
  22. }
  23. }
提交之前会将消息分组,就好物流分货一样,发往深圳放一堆。
  1. public <CResult> AsyncRequestFuture submit(ExecutorService pool, TableName tableName,
  2. List<? extends Row> rows, boolean atLeastOne, Batch.Callback<CResult> callback,
  3. boolean needResults) throws InterruptedIOException {
  4. ...
  5. Map<ServerName, MultiAction<Row>> actionsByServer =
  6. new HashMap<ServerName, MultiAction<Row>>();
  7. ....
  8. . do {
  9. //等待有空线程,如果没有会一直等下去
  10. waitForMaximumCurrentTasks(maxTotalConcurrentTasks - 1, tableName.getNameAsString());
  11.     .....
  12. RegionLocations locs = connection.locateRegion(
  13. tableName, r.getRow(), true, true, RegionReplicaUtil.DEFAULT_REPLICA_ID);
  14.     //这个方法,会将相同getServerName ,row 放在一起。
  15. addAction(loc.getServerName(), regionName, action, actionsByServer, nonceGroup);
  16. } while (retainedActions.isEmpty() && atLeastOne && (locationErrors == null));
  17. if (retainedActions.isEmpty()) return NO_REQS_RESULT;
  18. return submitMultiActions(tableName, retainedActions, nonceGroup, callback, null, needResults,
  19. locationErrors, locationErrorRows, actionsByServer, pool);
  20. }
发送前生成
  1. Runnable runnable =
  2. new SingleServerRequestRunnable(runner.getActions(), numAttempt, server,
都包含各种的需要发送的ROW 已经ServiceName
SingleServerRequestRunnable 实现的runnable ,有执行器执行。跑里面的run方法。在里面创建了callable,和caller
由caller调用call(callable).代码如下:caller很多地方将到来,所以不再详细说明。
  1. @Override
  2. public void run() {
  3.     ...//new 一个callable
  4. callable = createCallable(server, tableName, multiAction);
  5. ..//caller
  6. RpcRetryingCaller<MultiResponse> caller = createCaller(callable, rpcTimeout);
  7. ... //调用  
    1.    res = caller.callWithoutRetries(callable, operationTimeout);
  8.  ...
  9. // 检查返回结果
  10. receiveMultiAction(multiAction, server, res, numAttempt);
  11. ...
  12. }

在callable的call方法里有个
  1. responseProto = getStub().multi(controller, requestProto);
这里就是发送到RegionService。通过clientprotos 。

下面是服务端。
  1. @Override
  2. public MultiResponse multi(final RpcController rpcc, final MultiRequest request)
  3. throws ServiceException {
  4. ...
  5.     这里主要做些解析过程,已经认证。
  6. for (RegionAction regionAction : request.getRegionActionList()) {
  7. region = getRegion(regionAction.getRegion());
  8.        按region又分一次
  9. ClientProtos.RegionLoadStats stats = mutateRows(region, regionAction.getActionList(),
  10. cellScanner);
  11. }
  12. }
里面再建立批量RowMutations,而且不用区分delete和put
然后
  1. @Override
  2. public void processRowsWithLocks(RowProcessor<?,?> processor, long timeout,
  3. long nonceGroup, long nonce) throws IOException {
  4.     //主要是处理协处理钩子
  5. startRegionOperation();
  6. //行锁。
  7. Collection<byte[]> rowsToLock = processor.getRowsToLock();
  8.      //写WALKey
  9. doProcessRowWithTimeout(
  10. processor, now, this, mutations, walEdit, timeout);
  11.         //多版本
  12. // 7. Start mvcc transaction
  13. writeEntry = walKey.getWriteEntry();
  14. mvccNum = walKey.getSequenceId();
  15.         //写入内存
  16. // 8. Apply to memstore
  17. for (Mutation m : mutations) {
  18. for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
  19. Store store = getStore(cell);
  20. addedSize += store.add(cell);
  21. }
  22. }
  23. }
所以数据put都放在内存中。当addedSize的尺寸大于阈值时会spit到HFile里面。

这个后面再讲。

到此put过程结束。

未完待续...






原创粉丝点击