HBase createTable 的伺服器端實現源碼分析,hbasecreatetable
HBase的所有請求調用都是通過RPC的機制進行的,RPCServer監聽到請求之後會解析請求內容,然後根據解析的方法以及參數調用伺服器端實際的方法,這也是遠程代理模式的經典做法,createTable的請求最終實現是在HMaster中的,但是實際的表的建立過程是在CreateTableHandler類中的,接下來主要就HBase中表的建立過程進行詳細分析。
1. HMaster的createTable實現
如下代碼所示,是HMaster中的createTable的流程代碼:
public void createTable(HTableDescriptor hTableDescriptor, byte[][] splitKeys) throws IOException { if (isStopped()) { throw new MasterNotRunningException(); }String namespace = hTableDescriptor.getTableName().getNamespaceAsString(); ensureNamespaceExists(namespace); HRegionInfo[] newRegions = getHRegionInfos(hTableDescriptor, splitKeys); checkInitialized(); sanityCheckTableDescriptor(hTableDescriptor); if (cpHost != null) { cpHost.preCreateTable(hTableDescriptor, newRegions); } LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor); this.service.submit(new CreateTableHandler(this, this.fileSystemManager, hTableDescriptor, conf, newRegions, this).prepare()); if (cpHost != null) { cpHost.postCreateTable(hTableDescriptor, newRegions); } }
在正式建立表之前做的幾件事情:
1.檢查Master是否正常運行
2.檢查索要建立的表的namespace
是否存在
3.HRegionInfo
類包含了HRegion的相關資訊getHRegionInfos(),函數按照splitKeys和表描述資訊,擷取該表對應的HRegion的資訊。
這裡有必要解釋一下HRegion
,HRegion
儲存了table的資料資訊,它包含了每一個row的所有columns,一個table包含1到多個hregion,每一個hregion包含多個HStores
,每個HStores包含一部分的Rows和對應部分的Columns。
每個HRegion包含了一個[startKey, endKey),用來標識其儲存的row的範圍,英雌HRegion可以由TableName
和key range
唯一確定
4.檢查Master是否完成初始化
5.檢查表資訊是否符合規定
6.建表,建表又分為三個過程:
1. cpHost.preCreateTable(hTableDescriptor, newRegions);2. submit(new CreateTableHandler(this, this.fileSystemManager, hTableDescriptor, conf, newRegions, this)3.cpHost.postCreateTable(hTableDescriptor, newRegions);
其中步驟1和3都是為了副處理器預留的鉤子函數,方便應用開發人員動態添加新的功能。
接下來主要分析一下步驟2中的建表所做的操作
2. CreateTableHandler 中的建表實現
其實在代碼this.service.submit(new CreateTableHandler(this,
this.fileSystemManager, hTableDescriptor, conf,
newRegions, this).prepare());
中調用的過程分為兩個部分,一個是prepare,然後才是submit,先來看一下prepare()的工作
2.1 建表之前的準備工作prepare()分析
public CreateTableHandler prepare() throws NotAllMetaRegionsOnlineException, TableExistsException, IOException { // Need hbase:meta availability to create a table try { if (server.getMetaTableLocator().waitMetaRegionLocation( server.getZooKeeper(), timeout) == null) { throw new NotAllMetaRegionsOnlineException(); } // If we are creating the table in service to an RPC request, record the // active user for later, so proper permissions will be applied to the // new table by the AccessController if it is active if (RequestContext.isInRequestContext()) { this.activeUser = RequestContext.getRequestUser(); } else { this.activeUser = UserProvider.instantiate(conf).getCurrent(); } } //acquire the table write lock, blocking. Make sure that it is released. this.tableLock.acquire(); boolean success = false; try { TableName tableName = this.hTableDescriptor.getTableName(); if (MetaTableAccessor.tableExists(this.server.getConnection(), tableName)) { throw new TableExistsException(tableName); } checkAndSetEnablingTable(assignmentManager, tableName); success = true; } finally { if (!success) { releaseTableLock(); } } return this; }
這裡的主要工作如下:
1. 擷取hbase:meta 資訊,meta是hbase的一個特殊表,其存數了HBase上面的RegionServer的資訊以及其分布情況。
2. 檢查建立表的使用者的許可權並記錄。
3. 擷取table write鎖
4. 檢查表是否存在,已存在則拋出異常
5. 為了防止多個線程發起建同一個表的情況,可以在建表未成功之前可以先設定該table enable,這樣其他線程就不能再建表。
建表之前的準備工作到此結束,一下分析具體建表流程
2.2 建表具體實現handleCreateTable過程分析
handleCreateTable,其實主要做了三件事,1.在磁碟上建表,2.meta表,3.為建立的表分配對應的regionserver。詳細代碼如下:
這塊的代碼是分為八個小步驟,我們一一分析,
1.建立表描述符
// 1. Create Table DescriptorPath tempTableDir = FSUtils.getTableDir(tempdir, tableName);new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory(tempTableDir, this.hTableDescriptor, false);Path tableDir = FSUtils.getTableDir(fileSystemManager.getRootDir(), tableName);
首先建立一個臨時的檔案夾,然後建立對應的檔案表描述符,最後建立該表在檔案系統中的路徑,
HBase的表的資料對應在檔案系統中的一個檔案夾下,該檔案夾也就是表名
2.建立Regions
// 2. Create RegionsList<HRegionInfo> regionInfos = handleCreateHdfsRegions(tempdir, tableName);
為table 建立in-disk資料結構,內部具體建立了儲存table資料的HRegion,並返回hregioninfo的資訊
3.將步驟1中的臨時檔案夾移到HBase的根目錄下,如果hregions建立成功的話,繼續一下幾個步驟:
if (regionInfos != null && regionInfos.size() > 0) {// 4. Add regions to META addRegionsToMeta(regionInfos);// 5. Add replicas if needed regionInfos = addReplicas(hTableDescriptor, regionInfos);// 6. Trigger immediate assignment of the regions in round-robin fashion ModifyRegionUtils.assignRegions(assignmentManager, regionInfos);}
// 7. Set table enabled flag up in zk.try { assignmentManager.getTableStateManager().setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED); } catch (CoordinatedStateException e) { throw new IOException("Unable to ensure that " + tableName + " will be" + " enabled because of a ZooKeeper issue", e); }
8.跟新tabledescripter cache
// 8. Update the tabledescriptor cache.((HMaster) this.server).getTableDescriptors().get(tableName);
至此伺服器端資料庫表的建立過程源碼分析結束