一、Region 概念

Region是表获取和分布的基本元素,由每个列族的一个Store组成。对象层级图如下:

  1. Table (HBase table)
  2. Region (Regions for the table)
  3. Store (Store per ColumnFamily for each Region for the table)
  4. MemStore (MemStore for each Store for each Region for the table)
  5. StoreFile (StoreFiles for each Store for each Region for the table)
  6. 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配置文件中定义:

  1. <property>
  2. <name>hbase.hregion.max.filesize</name>
  3. <value>10 * 1024 * 1024 * 1024</value>
  4. </property>

说明:

  1. 当region中的StoreFile大小超过了上面配置的值的时候,该region就会被拆分,具体的拆分策略见下文。
  2. 上面的值也可以针对每个表单独设置,例如在hbase shell中设置:
  1. create 't','f'
  2. disable 't'
  3. alter 't', METHOD => 'table_att', MAX_FILESIZE => ''
  4. enable 't'

Region 拆分策略

Region的分割操作是不可见的,因为Master不会参与其中。RegionServer拆分region的步骤是,先将该region下线,然后拆分,将其子region加入到META元信息中,再将他们加入到原本的RegionServer中,最后汇报Master。

执行split的线程是CompactSplitThread。

自定义拆分策略

可以通过设置RegionSplitPolicy的实现类来指定拆分策略,RegionSplitPolicy类的实现类有:

  1. ConstantSizeRegionSplitPolicy
  2. IncreasingToUpperBoundRegionSplitPolicy
  3. DelimitedKeyPrefixRegionSplitPolicy
  4. 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:

  1. 第一次拆分大小为:min(10G1*1*128M)=128M
  2. 第二次拆分大小为:min(10G3*3*128M)=1152M
  3. 第三次拆分大小为:min(10G5*5*128M)=3200M
  4. 第四次拆分大小为:min(10G7*7*128M)=6272M
  5. 第五次拆分大小为:min(10G9*9*128M)=10G
  6. 第五次拆分大小为:min(10G11*11*128M)=10G

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

配置拆分策略

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

  1. // 更新现有表的split策略
  2. HBaseAdmin admin = new HBaseAdmin( conf);
  3. HTable hTable = new HTable( conf, "test" );
  4. HTableDescriptor htd = hTable.getTableDescriptor();
  5. HTableDescriptor newHtd = new HTableDescriptor(htd);
  6. newHtd.setValue(HTableDescriptor. SPLIT_POLICY, KeyPrefixRegionSplitPolicy.class.getName());// 指定策略
  7. newHtd.setValue("prefix_split_key_policy.prefix_length", "2");
  8. newHtd.setValue("MEMSTORE_FLUSHSIZE", "5242880"); // 5M
  9. admin.disableTable( "test");
  10. admin.modifyTable(Bytes. toBytes("test"), newHtd);
  11. 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的文件如下:

  1. 0001|
  2. 0002|
  3. 0003|
  4. 0004|
  5. 0005|
  6. 0006|
  7. 0007|
  8. 0008|
  9. 0009|

为什么后面会跟着一个"|",是因为在ASCII码中,"|"的值是124,大于所有的数字和字母等符号,当然也可以用“~”(ASCII-126)。分隔文件的第一行为第一个region的stopkey,每行依次类推,最后一行不仅是倒数第二个region的stopkey,同时也是最后一个region的startkey。也就是说分区文件中填的都是key取值范围的分隔点,如下图所示:

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

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

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

可以通过指定SPLITS_FILE的值指定分区文件,如果分区信息比较少,也可以直接用SPLITS分区。我们可以通过如下命令建一个分区表,指定第一步中生成的分区文件:

假如我还想对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范围比较随机,那么就可以解决写热点问题。
  1. long currentId = 1L;
  2. byte [] rowkey = Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId)).substring(0, 8).getBytes(),
  3. 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范围比较随机,那么就可以解决写热点问题。

  1. long currentId = 1L;
  2. byte [] rowkey = Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId)).substring(0, 8).getBytes(),
  3. 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

  1. public class HashChoreWoker implements SplitKeysCalculator{
  2. //随机取机数目
  3. private int baseRecord;
  4. //rowkey生成器
  5. private RowKeyGenerator rkGen;
  6. //取样时,由取样数目及region数相除所得的数量.
  7. private int splitKeysBase;
  8. //splitkeys个数
  9. private int splitKeysNumber;
  10. //由抽样计算出来的splitkeys结果
  11. private byte[][] splitKeys;
  12.  
  13. public HashChoreWoker(int baseRecord, int prepareRegions) {
  14. this.baseRecord = baseRecord;
  15. //实例化rowkey生成器
  16. rkGen = new HashRowKeyGenerator();
  17. splitKeysNumber = prepareRegions - 1;
  18. splitKeysBase = baseRecord / prepareRegions;
  19. }
  20.  
  21. public byte[][] calcSplitKeys() {
  22. splitKeys = new byte[splitKeysNumber][];
  23. //使用treeset保存抽样数据,已排序过
  24. TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
  25. for (int i = 0; i < baseRecord; i++) {
  26. rows.add(rkGen.nextId());
  27. }
  28. int pointer = 0;
  29. Iterator<byte[]> rowKeyIter = rows.iterator();
  30. int index = 0;
  31. while (rowKeyIter.hasNext()) {
  32. byte[] tempRow = rowKeyIter.next();
  33. rowKeyIter.remove();
  34. if ((pointer != 0) && (pointer % splitKeysBase == 0)) {
  35. if (index < splitKeysNumber) {
  36. splitKeys[index] = tempRow;
  37. index ++;
  38. }
  39. }
  40. pointer ++;
  41. }
  42. rows.clear();
  43. rows = null;
  44. return splitKeys;
  45. }
  46. }
  1. KeyGenerator及实现
  2. //interface
  3. public interface RowKeyGenerator {
  4. byte [] nextId();
  5. }
  6. //implements
  7. public class HashRowKeyGenerator implements RowKeyGenerator {
  8. private long currentId = 1;
  9. private long currentTime = System.currentTimeMillis();
  10. private Random random = new Random();
  11. public byte[] nextId() {
  12. try {
  13. currentTime += random.nextInt(1000);
  14. byte[] lowT = Bytes.copy(Bytes.toBytes(currentTime), 4, 4);
  15. byte[] lowU = Bytes.copy(Bytes.toBytes(currentId), 4, 4);
  16. return Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowU, lowT)).substring(0, 8).getBytes(),
  17. Bytes.toBytes(currentId));
  18. } finally {
  19. currentId++;
  20. }
  21. }
  22. }

unit test case测试

  1. @Test
  2. public void testHashAndCreateTable() throws Exception{
  3. HashChoreWoker worker = new HashChoreWoker(1000000,10);
  4. byte [][] splitKeys = worker.calcSplitKeys();
  5.  
  6. HBaseAdmin admin = new HBaseAdmin(HBaseConfiguration.create());
  7. TableName tableName = TableName.valueOf("hash_split_table");
  8.  
  9. if (admin.tableExists(tableName)) {
  10. try {
  11. admin.disableTable(tableName);
  12. } catch (Exception e) {
  13. }
  14. admin.deleteTable(tableName);
  15. }
  16.  
  17. HTableDescriptor tableDesc = new HTableDescriptor(tableName);
  18. HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("info"));
  19. columnDesc.setMaxVersions(1);
  20. tableDesc.addFamily(columnDesc);
  21.  
  22. admin.createTable(tableDesc ,splitKeys);
  23.  
  24. admin.close();
  25. }

查看建表结果:执行 scan 'hbase:meta'

  以上我们只是显示了部分region的信息,可以看到region的start-end key 还是比较随机散列的。同样可以查看hdfs的目录结构,的确和预期的38个预分区一致:

以上,就已经按hash方式,预建好了分区,以后在插入数据的时候,也要按照此rowkeyGenerator的方式生成rowkey,有兴趣的话,也可以做些试验,插入些数据,看看数据的分布。

二、partition

partition故名思义,就是分区式,这种分区有点类似于mapreduce中的partitioner,将区域用长整数(Long)作为分区号,每个region管理着相应的区域数据,在rowKey生成时,将id取模后,然后拼上id整体作为rowKey.这个比较简单,不需要取样,splitKeys也非常简单,直接是分区号即可。直接上代码吧:

  1. public class PartitionRowKeyManager implements RowKeyGenerator,
  2. SplitKeysCalculator {
  3.  
  4. public static final int DEFAULT_PARTITION_AMOUNT = 20;
  5. private long currentId = 1;
  6. private int partition = DEFAULT_PARTITION_AMOUNT;
  7. public void setPartition(int partition) {
  8. this.partition = partition;
  9. }
  10.  
  11. public byte[] nextId() {
  12. try {
  13. long partitionId = currentId % partition;
  14. return Bytes.add(Bytes.toBytes(partitionId),
  15. Bytes.toBytes(currentId));
  16. } finally {
  17. currentId++;
  18. }
  19. }
  20.  
  21. public byte[][] calcSplitKeys() {
  22. byte[][] splitKeys = new byte[partition - 1][];
  23. for(int i = 1; i < partition ; i ++) {
  24. splitKeys[i-1] = Bytes.toBytes((long)i);
  25. }
  26. return splitKeys;
  27. }
  28. }

calcSplitKeys方法比较单纯,splitKey就是partition的编号,我们看看测试类:

  1. @Test
  2. public void testPartitionAndCreateTable() throws Exception{
  3.  
  4. PartitionRowKeyManager rkManager = new PartitionRowKeyManager();
  5. //只预建10个分区
  6. rkManager.setPartition(10);
  7.  
  8. byte [][] splitKeys = rkManager.calcSplitKeys();
  9.  
  10. HBaseAdmin admin = new HBaseAdmin(HBaseConfiguration.create());
  11. TableName tableName = TableName.valueOf("partition_split_table");
  12.  
  13. if (admin.tableExists(tableName)) {
  14. try {
  15. admin.disableTable(tableName);
  16.  
  17. } catch (Exception e) {
  18. }
  19. admin.deleteTable(tableName);
  20. }
  21.  
  22. HTableDescriptor tableDesc = new HTableDescriptor(tableName);
  23. HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("info"));
  24. columnDesc.setMaxVersions(1);
  25. tableDesc.addFamily(columnDesc);
  26.  
  27. admin.createTable(tableDesc ,splitKeys);
  28.  
  29. admin.close();
  30. }

同样我们可以看看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的话,抽样和测试等,都是可以复用的。

如测试代码:

  1. public class HBaseSimulatorTest {
  2. //通过SPI方式获取HBaseSimulator实例,SPI的实现为simgple
  3. private HBaseSimulator hbase = BeanFactory.getInstance().getBeanInstance(HBaseSimulator.class);
  4. //获取RowKeyGenerator实例,SPI的实现为hashRowkey
  5. private RowKeyGenerator rkGen = BeanFactory.getInstance().getBeanInstance(RowKeyGenerator.class);
  6. //初如化苦工,去检测100w个抽样rowkey,然后生成一组splitKeys
  7. HashChoreWoker worker = new HashChoreWoker(1000000,10);
  8.  
  9. @Test
  10. public void testHash(){
  11. byte [][] splitKeys = worker.calcSplitKeys();
  12. hbase.createTable("user", splitKeys);
  13. //插入1亿条记录,看数据分布
  14. TableName tableName = TableName.valueOf("user");
  15. for(int i = 0; i < 100000000; i ++) {
  16. Put put = new Put(rkGen.nextId());
  17. hbase.put(tableName, put);
  18. }
  19. hbase.report(tableName);
  20. }
  21.  
  22. @Test
  23. public void testPartition(){
  24. //default 20 partitions.
  25. PartitionRowKeyManager rkManager = new PartitionRowKeyManager();
  26. byte [][] splitKeys = rkManager.calcSplitKeys();
  27.  
  28. hbase.createTable("person", splitKeys);
  29.  
  30. TableName tableName = TableName.valueOf("person");
  31. //插入1亿条记录,看数据分布
  32. for(int i = 0; i < 100000000; i ++) {
  33. Put put = new Put(rkManager.nextId());
  34. hbase.put(tableName, put);
  35. }
  36.  
  37. hbase.report(tableName);
  38. }
  39. }

执行结果:

  1. Execution Reprort:[StartRowkey:puts requsts:(put ratio)]
  2. :9973569:(1.0015434)
  3. 1986344a\x00\x00\x00\x00\x00\x01\x0E\xAE:9999295:(1.0041268)
  4. 331ee65f\x00\x00\x00\x00\x00\x0F)g:10012532:(1.005456)
  5. 4cbfd4f6\x00\x00\x00\x00\x00\x00o0:9975842:(1.0017716)
  6. 664c6388\x00\x00\x00\x00\x00\x02\x1Du:10053337:(1.0095537)
  7. 800945e0\x00\x00\x00\x00\x00\x01\xADV:9998719:(1.0040689)
  8. 99a158d9\x00\x00\x00\x00\x00\x0BZ\xF3:10000563:(1.0042541)
  9. b33a2223\x00\x00\x00\x00\x00\x07\xC6\xE6:9964921:(1.000675)
  10. ccbcf370\x00\x00\x00\x00\x00\x00*\xE2:9958200:(1.0)
  11. e63b8334\x00\x00\x00\x00\x00\x03g\xC1:10063022:(1.0105262)
  12. total requests:100000000
  13. Execution Reprort:[StartRowkey:puts requsts:(put ratio)]
  14. :5000000:(1.0)
  15. \x00\x00\x00\x00\x00\x00\x00\x01:5000000:(1.0)
  16. \x00\x00\x00\x00\x00\x00\x00\x02:5000000:(1.0)
  17. \x00\x00\x00\x00\x00\x00\x00\x03:5000000:(1.0)
  18. \x00\x00\x00\x00\x00\x00\x00\x04:5000000:(1.0)
  19. \x00\x00\x00\x00\x00\x00\x00\x05:5000000:(1.0)
  20. \x00\x00\x00\x00\x00\x00\x00\x06:5000000:(1.0)
  21. \x00\x00\x00\x00\x00\x00\x00\x07:5000000:(1.0)
  22. \x00\x00\x00\x00\x00\x00\x00\x08:5000000:(1.0)
  23. \x00\x00\x00\x00\x00\x00\x00\x09:5000000:(1.0)
  24. \x00\x00\x00\x00\x00\x00\x00\x0A:5000000:(1.0)
  25. \x00\x00\x00\x00\x00\x00\x00\x0B:5000000:(1.0)
  26. \x00\x00\x00\x00\x00\x00\x00\x0C:5000000:(1.0)
  27. \x00\x00\x00\x00\x00\x00\x00\x0D:5000000:(1.0)
  28. \x00\x00\x00\x00\x00\x00\x00\x0E:5000000:(1.0)
  29. \x00\x00\x00\x00\x00\x00\x00\x0F:5000000:(1.0)
  30. \x00\x00\x00\x00\x00\x00\x00\x10:5000000:(1.0)
  31. \x00\x00\x00\x00\x00\x00\x00\x11:5000000:(1.0)
  32. \x00\x00\x00\x00\x00\x00\x00\x12:5000000:(1.0)
  33. \x00\x00\x00\x00\x00\x00\x00\x13:5000000:(1.0)
  34. total requests:100000000

HBase之五:hbase的region分区的更多相关文章

  1. 【转帖】HBase之五:hbase的region分区

    HBase之五:hbase的region分区 https://www.cnblogs.com/duanxz/p/3154487.html 一.Region 概念 Region是表获取和分布的基本元素, ...

  2. Spark读Hbase优化 --手动划分region提高并行数

    一. Hbase的region 我们先简单介绍下Hbase的架构和Hbase的region: 从物理集群的角度看,Hbase集群中,由一个Hmaster管理多个HRegionServer,其中每个HR ...

  3. HBase单个RegionServer的region数目上限

    前言 RegionServer维护Master分配给它的region,处理对这些region的IO请求,负责切分在运行过程中变得过大的region, 由于集群性能( 分配的内存和磁盘是有限的 )有限的 ...

  4. hbase总结:如何监控region的性能

    转载:http://ju.outofmemory.cn/entry/50064 随着大数据表格应用的驱动,我们的HBase集群越来越大,然而由于机器.网络以及HBase内部的一些不确定性的bug,使得 ...

  5. HBase工具之监控Region的可用和读写延时状况

    1.介绍HBase集群上region数目由于业务驱动而越来越多,由于服务器本身,网络以及hbase内部的一些不确定性bug等因素使得这些region可能面临着不可用或响应延时情况.通过对region的 ...

  6. HBase篇--HBase常用优化

    一.前述 HBase优化能够让我们对调优有一定的理解,当然企业并不是所有的优化全都用,优化还要根据业务具体实施. 二.具体优化 1.表的设计  1.1 预分区 默认情况下,在创建HBase表的时候会自 ...

  7. Hbase理论&&hbase shell&&python操作hbase&&python通过mapreduce操作hbase

    一.Hbase搭建: 二.理论知识介绍: 1Hbase介绍: Hbase是分布式.面向列的开源数据库(其实准确的说是面向列族).HDFS为Hbase提供可靠的底层数据存储服务,MapReduce为Hb ...

  8. Hbase记录-Hbase介绍

    Hbase是什么 HBase是一种构建在HDFS之上的分布式.面向列的存储系统,适用于实时读写.随机访问超大规模数据的集群. HBase的特点 大:一个表可以有上亿行,上百万列. 面向列:面向列表(簇 ...

  9. Hbase总结(一)-hbase命令,hbase安装,与Hive的区别,与传统数据库的区别,Hbase数据模型

    Hbase总结(一)-hbase命令 下面我们看看HBase Shell的一些基本操作命令,我列出了几个常用的HBase Shell命令,如下: 名称 命令表达式 创建表 create '表名称', ...

  10. HBase学习-HBase原理

    1.系统架构 1.1 图解   从HBase的架构图上可以看出,HBase中的组件包括Client.Zookeeper.HMaster.HRegionServer.HRegion.Store.MemS ...

随机推荐

  1. hdu 2818 Building Block(并查集,有点点复杂)

    Building Block Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  2. 逐步实现hash算法(基于BKDRhash函数)

    哈希(Hash)算法,即散列函数.它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程.同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出.hash算法 ...

  3. Eclipse上安装springsource-tool-suite

    spring tool suite 是一个基于eclipseIDE开发环境中的用于开发spring应用程序的工具.提供了开箱即用的环境用于实现,调试和部署你的spring应用,包括为关键的的服务器和云 ...

  4. Android中破解应用签名校验的后续问题处理方案(闪退和重启现象以及无效问题)

    一.前言 之前已经写了一个爆破签名校验的工具kstools,很多同学也在使用,但是也反馈了不少问题,之前一篇文章也介绍了,关于爆破之后第三方登录问题修复,这篇我们在综合说明一下一些后遗症问题,关于ks ...

  5. IOS开发 多线程GCD

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispat ...

  6. Python3 移动文件——合集

    文件/文件夹操作头文件 import os import shutil 参考 Python3批量移动指定文件到指定文件夹

  7. 21天学通C++_Day5

    昨天停更了一天,真是羞羞啊,不过还是干了很多有意义的事的! 首先,昨天下午的时候,去参加了学校的春招!第一次参加招聘会,怕自己答不上面试官的问题,很是紧张! 和同学约的一点,结果到了发现还没开始,只能 ...

  8. Codeforces 1030E 【暴力构造】

    LINK 题目大意:给你n个数,你可以交换一个数的任意二进制位,问你可以选出多少区间经过操作后异或和是0 思路 充分必要条件: 区间中二进制1的个数是偶数 区间中二进制位最多的一个数的二进制个数小于等 ...

  9. Django(一):从socket到MVC

    一.socket的http套路 web应用本质上是一个socket服务端,用户的浏览器是一个socket客户端.socket处在应用层与传输层之间,是操作系统中I/O系统的延伸部分(接口),负责系统进 ...

  10. mysql拼接多条查询结果并且加序列

    SELECT GROUP_CONCAT(a.DESCRIPTION SEPARATOR '\n')     FROM (SELECT (@rowNum:=0) AS rowNo,CONCAT('公司内 ...