HBase之五:hbase的region分區


一、Region 概念

Region是表獲取和分布的基本元素,由每個列族的一個Store組成。對象層級圖如下:

Table       (HBase table)
    Region       (Regions for the table)
         Store          (Store per ColumnFamily for each Region for the table)
              MemStore        (MemStore for each Store for each Region for the table)
              StoreFile       (StoreFiles for each Store for each Region for the table)
                    Block     (Blocks within a StoreFile within a Store for each Region for the table)

Region 大小

Region的大小是一個棘手的問題,需要考量如下幾個因素。

  • Region是HBase中分布式存儲和負載均衡的最小單元。不同Region分布到不同RegionServer上,但並不是存儲的最小單元。
  • Region由一個或者多個Store組成,每個store保存一個columns family,每個Strore又由一個memStore和0至多個StoreFile 組成。memStore存儲在內存中, StoreFile存儲在HDFS上。
  • HBase通過將region切分在許多機器上實現分布式。也就是說,你如果有16GB的數據,只分了2個region, 你卻有20台機器,有18台就浪費了。
  • region數目太多就會造成性能下降,現在比以前好多了。但是對於同樣大小的數據,700個region比3000個要好。
  • region數目太少就會妨礙可擴展性,降低並行能力。有的時候導致壓力不夠分散。這就是為什么,你向一個10節點的HBase集群導入200MB的數據,大部分的節點是idle的。
  • RegionServer中1個region和10個region索引需要的內存量沒有太多的差別。

最好是使用默認的配置,可以把熱的表配小一點(或者受到split熱點的region把壓力分散到集群中)。如果你的cell的大小比較大(100KB或更大),就可以把region的大小調到1GB。region的最大大小在hbase配置文件中定義:

<property>
    <name>hbase.hregion.max.filesize</name>
    <value>10 * 1024 * 1024 * 1024</value>
  </property>

說明:

  1. 當region中的StoreFile大小超過了上面配置的值的時候,該region就會被拆分,具體的拆分策略見下文。
  2. 上面的值也可以針對每個表單獨設置,例如在hbase shell中設置:
create 't','f'
disable 't'
alter 't', METHOD => 'table_att', MAX_FILESIZE => '134217728'
enable 't'

Region 拆分策略

Region的分割操作是不可見的,因為Master不會參與其中。RegionServer拆分region的步驟是,先將該region下線,然后拆分,將其子region加入到META元信息中,再將他們加入到原本的RegionServer中,最后匯報Master。

執行split的線程是CompactSplitThread

自定義拆分策略

可以通過設置RegionSplitPolicy的實現類來指定拆分策略,RegionSplitPolicy類的實現類有:

ConstantSizeRegionSplitPolicy
    IncreasingToUpperBoundRegionSplitPolicy
        DelimitedKeyPrefixRegionSplitPolicy
        KeyPrefixRegionSplitPolicy

對於split,並不是設置了hbase.hregion.max.filesize(默認10G)為很大就保證不split了,需要有以下的算法:

  • IncreasingToUpperBoundRegionSplitPolicy,0.94.0默認region split策略。根據公式min(r^2*flushSize,maxFileSize)確定split的maxFileSize,其中r為在線region個數,maxFileSize由hbase.hregion.max.filesize指定。
  • ConstantSizeRegionSplitPolicy,僅僅當region大小超過常量值(hbase.hregion.max.filesize大小)時,才進行拆分。
  • DelimitedKeyPrefixRegionSplitPolicy,保證以分隔符前面的前綴為splitPoint,保證相同RowKey前綴的數據在一個Region中
  • KeyPrefixRegionSplitPolicy,保證具有相同前綴的row在一個region中(要求設計中前綴具有同樣長度)。指定rowkey前綴位數划分region,通過讀取table的prefix_split_key_policy.prefix_length屬性,該屬性為數字類型,表示前綴長度,在進行split時,按此長度對splitPoint進行截取。此種策略比較適合固定前綴的rowkey。當table中沒有設置該屬性,或其屬性不為Integer類型時,指定此策略效果等同與使用IncreasingToUpperBoundRegionSplitPolicy。

IncreasingToUpperBoundRegionSplitPolicy

這是0.94.0默認region split策略。根據根據公式min(r^2*flushSize,maxFileSize)確定split的maxFileSize,這里假設flushSize為128M:

第一次拆分大小為:min(10G,1*1*128M)=128M
第二次拆分大小為:min(10G,3*3*128M)=1152M
第三次拆分大小為:min(10G,5*5*128M)=3200M
第四次拆分大小為:min(10G,7*7*128M)=6272M
第五次拆分大小為:min(10G,9*9*128M)=10G
第五次拆分大小為:min(10G,11*11*128M)=10G

可以看到,只有在第五次之后的拆分大小才為10G

配置拆分策略

你可以在hbase配置文件中定義全局的拆分策略,設置hbase.regionserver.region.split.policy的值即可,也可以在創建和修改表時候指定:

// 更新現有表的split策略
HBaseAdmin admin = new HBaseAdmin( conf);
HTable hTable = new HTable( conf, "test" );
HTableDescriptor htd = hTable.getTableDescriptor();
HTableDescriptor newHtd = new HTableDescriptor(htd);
newHtd.setValue(HTableDescriptor. SPLIT_POLICY, KeyPrefixRegionSplitPolicy.class.getName());// 指定策略
newHtd.setValue("prefix_split_key_policy.prefix_length", "2");
newHtd.setValue("MEMSTORE_FLUSHSIZE", "5242880"); // 5M
admin.disableTable( "test");
admin.modifyTable(Bytes. toBytes("test"), newHtd);
admin.enableTable( "test");

說明:

  1. 上面的不同策略可以在不同的業務場景下使用,特別是第三種和第四種一般關注和使用的比較少。
  2. 如果想關閉自動拆分改為手動拆分,建議同時修改hbase.hregion.max.filesizehbase.regionserver.region.split.policy值。

二、hbase預分區示例

步驟:

1.規划hbase預分區

首先就是要想明白數據的key是如何分布的,然后規划一下要分成多少region,每個region的startkey和endkey是多少,然后將規划的key寫到一個文件中。比如,key的前幾位字符串都是從0001~0010的數字,這樣可以分成10個region,划分key的文件如下:

0001|  
0002|  
0003|  
0004|  
0005|  
0006|  
0007|  
0008|  
0009| 

為什么后面會跟着一個"|",是因為在ASCII碼中,"|"的值是124,大於所有的數字和字母等符號,當然也可以用“~”(ASCII-126)。分隔文件的第一行為第一個region的stopkey,每行依次類推,最后一行不僅是倒數第二個region的stopkey,同時也是最后一個region的startkey。也就是說分區文件中填的都是key取值范圍的分隔點,如下圖所示:

2.hbase shell中建分區表,指定分區文件

在hbase shell中直接輸入create,會看到如下的提示:

Create a table with namespace=ns1 and table qualifier=t1  
  hbase> create 'ns1:t1', {NAME => 'f1', VERSIONS => 5}  
  
Create a table with namespace=default and table qualifier=t1  
  hbase> create 't1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}  
  hbase> # The above in shorthand would be the following:  
  hbase> create 't1', 'f1', 'f2', 'f3'  
  hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}  
  hbase> create 't1', {NAME => 'f1', CONFIGURATION => {'hbase.hstore.blockingStoreFiles' => '10'}}  
    
Table configuration options can be put at the end.  
Examples:  
  
  hbase> create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']  
  hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']  
  hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt', OWNER => 'johndoe'  
  hbase> create 't1', {NAME => 'f1', VERSIONS => 5}, METADATA => { 'mykey' => 'myvalue' }  
  hbase> # Optionally pre-split the table into NUMREGIONS, using  
  hbase> # SPLITALGO ("HexStringSplit", "UniformSplit" or classname)  
  hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}  
  hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit', CONFIGURATION => {'hbase.hregion.scan.loadColumnFamiliesOnDemand' => 'true'}}  
  hbase> create 't1', {NAME => 'f1'}, {NAME => 'if1', LOCAL_INDEX=>'COMBINE_INDEX|INDEXED=f1:q1:8|rowKey:rowKey:10,UPDATE=true'} 

可以通過指定SPLITS_FILE的值指定分區文件,如果分區信息比較少,也可以直接用SPLITS分區。我們可以通過如下命令建一個分區表,指定第一步中生成的分區文件:

create 'split_table_test', 'cf', {SPLITS_FILE => 'region_split_info.txt'} 

假如我還想對hbase表做一個SNAPPY壓縮,應該怎么寫呢?

  1. create 'split_table_test',{NAME =>'cf', COMPRESSION => 'SNAPPY'}, {SPLITS_FILE => 'region_split_info.txt'}  

這里注意,一定要將分區的參數指定單獨用一個大括號擴起來,因為分區是針對全表,而不是針對某一個column family。

 

下面,我們登陸一下master的web頁面<Hmaster:60010>,查看一下hbase的表信息,找到剛剛新建的預分區表,進入查看region信息:

我們看到第一個region是沒有startkey的,最后一個region是沒有stopkey的。

 

三、hbase預分區方案

  在HBase中,表會被划分為1...n個Region,被托管在RegionServer中。Region二個重要的屬性:StartKey與EndKey表示這個Region維護的rowKey范圍,當我們要讀/寫數據時,如果rowKey落在某個start-end key范圍內,那么就會定位到目標region並且讀/寫到相關的數據。

1、由於業務數據一般都是從小到大增長的,根據上面hbase的region規則,就會出現“熱點寫”問題,隨着系統的運營,數據總是會往最大的start-key所在的region里寫,因為我們的rowkey總是會比之前的大,並且hbase的是按升序方式排序的。所以寫操作總是被定位到無上界的那個region中。
2、其次,由於寫熱點,我們總是往最大start-key的region寫記錄,之前分裂出來的region不會再被寫數據,有點被打進冷宮的趕腳,它們都處於半滿狀態,這樣的分布也是不利的。
如果在寫比較頻率的場景下,數據增長快,split的次數也會增多,由於split是比較耗時耗資源的,所以我們並不希望這種事情經常發生。

看到這些缺點,我們知道,在集群的環境中,為了得到更好的並行性,我們希望有好的load blance,讓每個節點提供的請求處理都是均等的。我們也希望,region不要經常split,因為split會使server有一段時間的停頓,如何能做到呢?
隨機散列與預分區

隨機散列與預分區:二者結合起來,是比較完美的,預分區一開始就預建好了一部分region,這些region都維護着自已的start-end keys,再配合上隨機散列,寫數據能均等地命中這些預建的region,就能解決上面的那些缺點,大大地提高了性能。

提供2種思路: hash與partition.

  1. hash就是rowkey前面由一串隨機字符串組成,隨機字符串生成方式可以由SHA或者MD5等方式生成,只要region所管理的start-end keys范圍比較隨機,那么就可以解決寫熱點問題。
long currentId = 1L;
byte [] rowkey = Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId)).substring(0, 8).getBytes(),
Bytes.toBytes(currentId));

假設rowKey原本是自增長的long型,可以將rowkey轉為hash再轉為bytes,加上本身id 轉為bytes,組成rowkey,這樣就生成隨便的rowkey。那么對於這種方式的rowkey設計,如何去進行預分區呢?
1.取樣,先隨機生成一定數量的rowkey,將取樣數據按升序排序放到一個集合里
2.根據預分區的region個數,對整個集合平均分割,即是相關的splitKeys.
3.HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][] splitkeys)可以指定預分區的splitKey,即是指定region間的rowkey臨界值.

 

首先是熱點寫,我們總是會往最大的start-key所在的region寫東西,因為我們的rowkey總是會比之前的大,並且hbase的是按升序方式排序的。所以寫操作總是被定位到無上界的那個region中。
    其次,由於寫熱點,我們總是往最大start-key的region寫記錄,之前分裂出來的region不會再被寫數據,有點被打進冷宮的趕腳,它們都處於半滿狀態,這樣的分布也是不利的。
    如果在寫比較頻率的場景下,數據增長快,split的次數也會增多,由於split是比較耗時耗資源的,所以我們並不希望這種事情經常發生。
    ............


    看到這些缺點,我們知道,在集群的環境中,為了得到更好的並行性,我們希望有好的load blance,讓每個節點提供的請求處理都是均等的。我們也希望,region不要經常split,因為split會使server有一段時間的停頓,如何能做到呢?
隨機散列與預分區。二者結合起來,是比較完美的,預分區一開始就預建好了一部分region,這些region都維護着自已的start-end keys,再配合上隨機散列,寫數據能均等地命中這些預建的region,就能解決上面的那些缺點,大大地提高了性能。

提供2種思路: hash 與 partition. 

一、hash思路

  hash就是rowkey前面由一串隨機字符串組成,隨機字符串生成方式可以由SHA或者MD5等方式生成,只要region所管理的start-end keys范圍比較隨機,那么就可以解決寫熱點問題。

long currentId = 1L;
byte [] rowkey = Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId)).substring(0, 8).getBytes(),
                    Bytes.toBytes(currentId));

假設rowKey原本是自增長的long型,可以將rowkey轉為hash再轉為bytes,加上本身id 轉為bytes,組成rowkey,這樣就生成隨便的rowkey。那么對於這種方式的rowkey設計,如何去進行預分區呢?
    1.取樣,先隨機生成一定數量的rowkey,將取樣數據按升序排序放到一個集合里
    2.根據預分區的region個數,對整個集合平均分割,即是相關的splitKeys.
    3.HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][] splitkeys)可以指定預分區的splitKey,即是指定region間的rowkey臨界值.

如果知道Hbase數據表的key的分布情況,就可以在建表的時候對hbase進行region的預分區。這樣做的好處是防止大數據量插入的熱點問題,提高數據插入的效率。

步驟:

1.創建split計算器,用於從抽樣數據中生成一個比較合適的splitKeys

public class HashChoreWoker implements SplitKeysCalculator{
    //隨機取機數目
    private int baseRecord;
    //rowkey生成器
    private RowKeyGenerator rkGen;
    //取樣時,由取樣數目及region數相除所得的數量.
    private int splitKeysBase;
    //splitkeys個數
    private int splitKeysNumber;
    //由抽樣計算出來的splitkeys結果
    private byte[][] splitKeys;

    public HashChoreWoker(int baseRecord, int prepareRegions) {
        this.baseRecord = baseRecord;
        //實例化rowkey生成器
        rkGen = new HashRowKeyGenerator();
        splitKeysNumber = prepareRegions - 1;
        splitKeysBase = baseRecord / prepareRegions;
    }

    public byte[][] calcSplitKeys() {
        splitKeys = new byte[splitKeysNumber][];
        //使用treeset保存抽樣數據,已排序過
        TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
        for (int i = 0; i < baseRecord; i++) {
            rows.add(rkGen.nextId());
        }
        int pointer = 0;
        Iterator<byte[]> rowKeyIter = rows.iterator();
        int index = 0;
        while (rowKeyIter.hasNext()) {
            byte[] tempRow = rowKeyIter.next();
            rowKeyIter.remove();
            if ((pointer != 0) && (pointer % splitKeysBase == 0)) {
                if (index < splitKeysNumber) {
                    splitKeys[index] = tempRow;
                    index ++;
                }
            }
            pointer ++;
        }
        rows.clear();
        rows = null;
        return splitKeys;
    }
}
KeyGenerator及實現
//interface
public interface RowKeyGenerator {
    byte [] nextId();
}
//implements
public class HashRowKeyGenerator implements RowKeyGenerator {
    private long currentId = 1;
    private long currentTime = System.currentTimeMillis();
    private Random random = new Random();
    public byte[] nextId() {
        try {
            currentTime += random.nextInt(1000);
            byte[] lowT = Bytes.copy(Bytes.toBytes(currentTime), 4, 4);
            byte[] lowU = Bytes.copy(Bytes.toBytes(currentId), 4, 4);
            return Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowU, lowT)).substring(0, 8).getBytes(),
                    Bytes.toBytes(currentId));
        } finally {
            currentId++;
        }
    }
}

unit test case測試

@Test
public void testHashAndCreateTable() throws Exception{
        HashChoreWoker worker = new HashChoreWoker(1000000,10);
        byte [][] splitKeys = worker.calcSplitKeys();
        
        HBaseAdmin admin = new HBaseAdmin(HBaseConfiguration.create());
        TableName tableName = TableName.valueOf("hash_split_table");
        
        if (admin.tableExists(tableName)) {
            try {
                admin.disableTable(tableName);
            } catch (Exception e) {
            }
            admin.deleteTable(tableName);
        }

        HTableDescriptor tableDesc = new HTableDescriptor(tableName);
        HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("info"));
        columnDesc.setMaxVersions(1);
        tableDesc.addFamily(columnDesc);

        admin.createTable(tableDesc ,splitKeys);

        admin.close();
    }

查看建表結果:執行 scan 'hbase:meta'

  以上我們只是顯示了部分region的信息,可以看到region的start-end key 還是比較隨機散列的。同樣可以查看hdfs的目錄結構,的確和預期的38個預分區一致:

  以上,就已經按hash方式,預建好了分區,以后在插入數據的時候,也要按照此rowkeyGenerator的方式生成rowkey,有興趣的話,也可以做些試驗,插入些數據,看看數據的分布。

二、partition

     partition故名思義,就是分區式,這種分區有點類似於mapreduce中的partitioner,將區域用長整數(Long)作為分區號,每個region管理着相應的區域數據,在rowKey生成時,將id取模后,然后拼上id整體作為rowKey.這個比較簡單,不需要取樣,splitKeys也非常簡單,直接是分區號即可。直接上代碼吧:

public class PartitionRowKeyManager implements RowKeyGenerator,
        SplitKeysCalculator {

    public static final int DEFAULT_PARTITION_AMOUNT = 20;
    private long currentId = 1;
    private int partition = DEFAULT_PARTITION_AMOUNT;
    public void setPartition(int partition) {
        this.partition = partition;
    }

    public byte[] nextId() {
        try {
            long partitionId = currentId % partition;
            return Bytes.add(Bytes.toBytes(partitionId),
                    Bytes.toBytes(currentId));
        } finally {
            currentId++;
        }
    }

    public byte[][] calcSplitKeys() {
        byte[][] splitKeys = new byte[partition - 1][];
        for(int i = 1; i < partition ; i ++) {
            splitKeys[i-1] = Bytes.toBytes((long)i);
        }
        return splitKeys;
    }
}

calcSplitKeys方法比較單純,splitKey就是partition的編號,我們看看測試類:

@Test
    public void testPartitionAndCreateTable() throws Exception{
        
        PartitionRowKeyManager rkManager = new PartitionRowKeyManager();
        //只預建10個分區
        rkManager.setPartition(10);
        
        byte [][] splitKeys = rkManager.calcSplitKeys();
        
        HBaseAdmin admin = new HBaseAdmin(HBaseConfiguration.create());
        TableName tableName = TableName.valueOf("partition_split_table");
        
        if (admin.tableExists(tableName)) {
            try {
                admin.disableTable(tableName);

            } catch (Exception e) {
            }
            admin.deleteTable(tableName);
        }

        HTableDescriptor tableDesc = new HTableDescriptor(tableName);
        HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("info"));
        columnDesc.setMaxVersions(1);
        tableDesc.addFamily(columnDesc);

        admin.createTable(tableDesc ,splitKeys);

        admin.close();
    }

同樣我們可以看看meta表和hdfs的目錄結果,其實和hash類似,region都會分好區,在這里就不上圖了。

三、總結     

通過partition實現的loadblance寫的話,當然生成rowkey方式也要結合當前的region數目取模而求得,大家同樣也可以做些實驗,看看數據插入后的分布。
在這里也順提一下,如果是順序的增長型原id,可以將id保存到一個數據庫,傳統的也好,redis的也好,每次取的時候,將數值設大1000左右,以后id可以在內存內增長,當內存數量已經超過1000的話,再去load下一個,有點類似於oracle中的sqeuence.

     隨機分布加預分區也不是一勞永逸的。因為數據是不斷地增長的,隨着時間不斷地推移,已經分好的區域,或許已經裝不住更多的數據,當然就要進一步進行split了,同樣也會出現性能損耗問題,所以我們還是要規划好數據增長速率,觀察好數據定期維護,按需分析是否要進一步分行手工將分區再分好,也或者是更嚴重的是新建表,做好更大的預分區然后進行數據遷移。小吳只是菜鳥,運維方面也只是自已這樣認為而已,供大家作簡單的參考吧。如果數據裝不住了,對於partition方式預分區的話,如果讓它自然分裂的話,情況分嚴重一點。因為分裂出來的分區號會是一樣的,所以計算到partitionId的話,其實還是回到了順序寫年代,會有部分熱點寫問題出現,如果使用partition方式生成主鍵的話,數據增長后就要不斷地調整分區了,比如增多預分區,或者加入子分區號的處理.(我們的分區號為long型,可以將它作為多級partition)

    

     OK,寫到這里,基本已經講完了防止熱點寫使用的方法和防止頻繁split而采取的預分區。但rowkey設計,遠遠也不止這些,比如rowkey長度,然后它的長度最大可以為char的MAXVALUE,但是看過之前我寫KeyValue的分析知道,我們的數據都是以KeyValue方式存儲在MemStore或者HFile中的,每個KeyValue都會存儲rowKey的信息,如果rowkey太大的話,比如是128個字節,一行10個字段的表,100萬行記錄,光rowkey就占了1.2G+所以長度還是不要過長,另外設計,還是按需求來吧。

     最后題外話是我想分享我在github中建了一個project,希望做一些hbase一些工具:https://github.com/bdifn/hbase-tools,如果本地裝了git的話,可以執行命令: git clone https://github.com/bdifn/hbase-tools.git目前加了一個region-helper子項目,也是目前唯一的一個子項目,項目使用maven管理,主要目的是幫助我們設計rowkey做一些參考,比如我們設計的隨機寫和預分區測試,提供了抽樣的功能,提供了檢測隨機寫的功能,然后統計按目前rowkey設計,隨機寫n條記錄后,統計每個region的記錄數,然后顯示比例等。
     測試仿真模塊我程為simualtor,主要是模擬hbase的region行為,simple的實現,僅僅是上面提到的預測我們rowkey設計后,建好預分區后,寫數據的的分布比例,而emulation是比較逼真的仿真,設想是我們寫數據時,會統計數目的大小,根據我們的hbase-site.xml設定,模擬memStore行為,模擬hfile的行為,最終會生成一份表的報表,比如分區的數據大小,是否split了,等等,以供我們去設計hbase表時有一個參考,但是遺憾的是,由於時間關系,我只花了一點業余時間簡單搭了一下框架,目前沒有更一步的實現,以后有時間再加以完善,當然也歡迎大家一起加入,一起學習吧。

     

     項目使用maven管理,為了方便測試,一些組件的實例化,我使用了java的SPI,download源碼后,如果想測試自已的rowKeyGeneator的話,打開com.bdifn.hbasetools.regionhelper.rowkey.RowKeyGenerator文件后,替換到你們的ID生成器就可以了。如果是hash的話,抽樣和測試等,都是可以復用的。

    如測試代碼:

復制代碼
public class HBaseSimulatorTest {
    //通過SPI方式獲取HBaseSimulator實例,SPI的實現為simgple
    private  HBaseSimulator hbase = BeanFactory.getInstance().getBeanInstance(HBaseSimulator.class);
    //獲取RowKeyGenerator實例,SPI的實現為hashRowkey
    private RowKeyGenerator rkGen = BeanFactory.getInstance().getBeanInstance(RowKeyGenerator.class);
    //初如化苦工,去檢測100w個抽樣rowkey,然后生成一組splitKeys
    HashChoreWoker worker = new HashChoreWoker(1000000,10);
    
    @Test
    public void testHash(){
        byte [][] splitKeys = worker.calcSplitKeys();
        hbase.createTable("user", splitKeys);
        //插入1億條記錄,看數據分布
        TableName tableName = TableName.valueOf("user");
        for(int i = 0; i < 100000000; i ++) {
            Put put = new Put(rkGen.nextId());
            hbase.put(tableName, put);
        }
        hbase.report(tableName);
    }
    
    @Test
    public void testPartition(){
        //default 20 partitions.
        PartitionRowKeyManager rkManager = new PartitionRowKeyManager();
        byte [][] splitKeys = rkManager.calcSplitKeys();
        
        hbase.createTable("person", splitKeys);
        
        TableName tableName = TableName.valueOf("person");
        //插入1億條記錄,看數據分布
        for(int i = 0; i < 100000000; i ++) {
            Put put = new Put(rkManager.nextId());
            hbase.put(tableName, put);
        }
        
        hbase.report(tableName);
    }
}
復制代碼

    執行結果:

    

復制代碼
Execution Reprort:[StartRowkey:puts requsts:(put ratio)]
:9973569:(1.0015434)
1986344a\x00\x00\x00\x00\x00\x01\x0E\xAE:9999295:(1.0041268)
331ee65f\x00\x00\x00\x00\x00\x0F)g:10012532:(1.005456)
4cbfd4f6\x00\x00\x00\x00\x00\x00o0:9975842:(1.0017716)
664c6388\x00\x00\x00\x00\x00\x02\x1Du:10053337:(1.0095537)
800945e0\x00\x00\x00\x00\x00\x01\xADV:9998719:(1.0040689)
99a158d9\x00\x00\x00\x00\x00\x0BZ\xF3:10000563:(1.0042541)
b33a2223\x00\x00\x00\x00\x00\x07\xC6\xE6:9964921:(1.000675)
ccbcf370\x00\x00\x00\x00\x00\x00*\xE2:9958200:(1.0)
e63b8334\x00\x00\x00\x00\x00\x03g\xC1:10063022:(1.0105262)
total requests:100000000
Execution Reprort:[StartRowkey:puts requsts:(put ratio)]
:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x01:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x02:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x03:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x04:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x05:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x06:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x07:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x08:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x09:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0A:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0B:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0C:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0D:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0E:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x0F:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x10:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x11:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x12:5000000:(1.0)
\x00\x00\x00\x00\x00\x00\x00\x13:5000000:(1.0)
total requests:100000000
復制代碼

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM