HBase rowkey设计-热点问题

        当处理由连续事件得到的数据时,即时间上连续的数据。这些数据可能来自于某个传感器网络、证券交易或者一个监控系统。它们显著的特点就是rowkey中含有事件发生时间。带来的一个问题便是HBase对于row的不均衡分布,它们被存储在一个唯一的rowkey区间中,被称为region,区间的范围被称为Start Key和End Key。
        如果将单调递增的时间类型数据作为rowkey,value很容易被散列到同一个Region中,这样它们会被存储在同一个服务器上,从而所有的访问和更新操作都会集中到这一台服务器上,从而在集群中形成一个hot spot,从而不能将集群的整体性能发挥出来。

        要解决这个问题是非常容易的,只需要将所有的数据散列到全部的Region上即可。这是可以做到的,在rowkey前面加上一个非线性前缀,或者翻转rowkey,或者将rowkey hash化。

        数据分散到不同的Region上存储,可以利用HBase的并行特点,可以利用MapReduce和spark计算框架并行处理数据。

下列代码是参考(在rowkey前面加上一个非线性前缀)

  1. import java.io.IOException;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4.   
  5. import org.apache.hadoop.conf.Configuration;  
  6. import org.apache.hadoop.hbase.HBaseConfiguration;  
  7. import org.apache.hadoop.hbase.HColumnDescriptor;  
  8. import org.apache.hadoop.hbase.HTableDescriptor;  
  9. import org.apache.hadoop.hbase.KeyValue;  
  10. import org.apache.hadoop.hbase.MasterNotRunningException;  
  11. import org.apache.hadoop.hbase.TableName;  
  12. import org.apache.hadoop.hbase.ZooKeeperConnectionException;  
  13. import org.apache.hadoop.hbase.client.Get;  
  14. import org.apache.hadoop.hbase.client.HBaseAdmin;  
  15. import org.apache.hadoop.hbase.client.HTable;  
  16. import org.apache.hadoop.hbase.client.HTablePool;  
  17. import org.apache.hadoop.hbase.client.Put;  
  18. import org.apache.hadoop.hbase.client.Result;  
  19. import org.apache.hadoop.hbase.client.ResultScanner;  
  20. import org.apache.hadoop.hbase.client.Scan;  
  21. import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;  
  22. import org.apache.hadoop.hbase.filter.Filter;  
  23. import org.apache.hadoop.hbase.filter.FilterList;  
  24. import org.apache.hadoop.hbase.filter.PrefixFilter;  
  25. import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;  
  26. import org.apache.hadoop.hbase.util.Bytes;  
  27. import org.slf4j.Logger;  
  28. import org.slf4j.LoggerFactory;  
  29.   
  30. import com.kktest.hbase.HashChoreWoker;  
  31. import com.kktest.hbase.HashRowKeyGenerator;  
  32. import com.kktest.hbase.RowKeyGenerator;  
  33. import com.kktest.hbase.BitUtils;  
  34.   
  35. /** 
  36.  * hbase 客户端 
  37.  *  
  38.  * @author kuang hj 
  39.  *  
  40.  */  
  41. @SuppressWarnings("all")  
  42. public class HBaseClient {  
  43.   
  44.     private static Logger logger = LoggerFactory.getLogger(HBaseClient.class);  
  45.     private static Configuration config;  
  46.     static {  
  47.         config = HBaseConfiguration.create();  
  48.         config.set("hbase.zookeeper.quorum",  
  49.                 "192.168.1.100:2181,192.168.1.101:2181,192.168.1.103:2181");  
  50.     }  
  51.   
  52.     /** 
  53.      * 根据随机散列(hash)创建分区表 
  54.      *  
  55.      * @throws Exception 
  56.      *             hash_split_table 
  57.      */  
  58.     public static void testHashAndCreateTable(String tableNameTmp,  
  59.             String columnFamily) throws Exception {        // 取随机散列 10 代表 10个分区  
  60.         HashChoreWoker worker = new HashChoreWoker(100000010);  
  61.         byte[][] splitKeys = worker.calcSplitKeys();  
  62.   
  63.         HBaseAdmin admin = new HBaseAdmin(config);  
  64.         TableName tableName = TableName.valueOf(tableNameTmp);  
  65.   
  66.         if (admin.tableExists(tableName)) {  
  67.             try {  
  68.                 admin.disableTable(tableName);  
  69.             } catch (Exception e) {  
  70.             }  
  71.             admin.deleteTable(tableName);  
  72.         }  
  73.   
  74.         HTableDescriptor tableDesc = new HTableDescriptor(tableName);  
  75.         HColumnDescriptor columnDesc = new HColumnDescriptor(  
  76.                 Bytes.toBytes(columnFamily));  
  77.         columnDesc.setMaxVersions(1);  
  78.         tableDesc.addFamily(columnDesc);  
  79.   
  80.         admin.createTable(tableDesc, splitKeys);  
  81.   
  82.         admin.close();  
  83.     }  
  84.   
  85.     /** 
  86.      * @Title: queryData 
  87.      * @Description: 从HBase查询出数据 
  88.      * @author kuang hj 
  89.      * @param tableName 
  90.      *            表名 
  91.      * @param rowkey 
  92.      *            rowkey 
  93.      * @return 返回用户信息的list 
  94.      * @throws Exception 
  95.      */  
  96.     @SuppressWarnings("all")  
  97.     public static ArrayList<String> queryData(String tableName, String rowkey)  
  98.             throws Exception {  
  99.         ArrayList<String> list = new ArrayList<String>();  
  100.         logger.info("开始时间");  
  101.         //在高并发的情况下,最好不要使用HTable/HTablePool,用asynchbase
  102.         HTable table = new HTable(config, tableName);  
  103.   
  104.         Get get = new Get(rowkey.getBytes()); // 根据rowkey查询,该操作会比较费时  
  105.         Result r = table.get(get);  
  106.         logger.info("结束时间");  
  107.         KeyValue[] kv = r.raw();  
  108.         for (int i = 0; i < kv.length; i++) {  
  109.             // 循环每一列  
  110.             String key = kv[i].getKeyString();  
  111.               
  112.             String value = kv[i].getValueArray().toString();  
  113.               
  114.             // 将查询到的结果写入List中  
  115.             list.add(key + ":"+ value);  
  116.               
  117.         }// end of 遍历每一列  
  118.           
  119.         return list;  
  120.     }  
  121.   
  122.     /** 
  123.      * 增加表数据 
  124.      *  
  125.      * @param tableName 
  126.      * @param rowkey 
  127.      */  
  128.     public static void insertData(String tableName, String rowkey) {  
  129.         HTable table = null;  
  130.         try {  
  131.             table = new HTable(config, tableName);  
  132.             // 一个PUT代表一行数据,再NEW一个PUT表示第二行数据,每行一个唯一的ROWKEY,此处rowkey为put构造方法中传入的值  
  133.             for (int i = 1; i < 100; i++) {  
  134.                 byte[] result = getNumRowkey(rowkey,i);  
  135.                 Put put = new Put(result);  
  136.                 // 本行数据的第一列  
  137.                 put.add(rowkey.getBytes(), "name".getBytes(),  
  138.                         ("aaa" + i).getBytes());  
  139.                 // 本行数据的第三列  
  140.                 put.add(rowkey.getBytes(), "age".getBytes(),  
  141.                         ("bbb" + i).getBytes());  
  142.                 // 本行数据的第三列  
  143.                 put.add(rowkey.getBytes(), "address".getBytes(),  
  144.                         ("ccc" + i).getBytes());  
  145.   
  146.                 table.put(put);  
  147.             }  
  148.   
  149.         } catch (Exception e1) {  
  150.             e1.printStackTrace();  
  151.         }  
  152.     }  
  153.   
  154.    //在旧的rowkey前加上hash值,产生新的rowkey
  155.     private static byte[] getNewRowkey(String rowkey) {  
  156.         byte[] result = null;  
  157.   
  158.         //hash值生成器
  159.         RowKeyGenerator rkGen = new HashRowKeyGenerator();  
  160.         byte[] splitKeys = rkGen.nextId();  
  161.   
  162.         byte[] rowkeytmp = rowkey.getBytes();  
  163.   
  164.        //hash值字符串+旧rowkey=新rowkey
  165.         result = new byte[splitKeys.length + rowkeytmp.length];  
  166.         System.arraycopy(splitKeys, 0, result, 0, splitKeys.length);  
  167.         System.arraycopy(rowkeytmp, 0, result, splitKeys.length,  
  168.                 rowkeytmp.length);  
  169.   
  170.         return result;  
  171.     }  
  172.       
  173.     public static void main(String[] args) {  
  174.         RowKeyGenerator rkGen = new HashRowKeyGenerator();  
  175.         byte[] splitKeys = rkGen.nextId();  
  176.         System.out.println(splitKeys);      
  177.     }  
  178.   
  179.     //与getNewRowkey类似
  180.     private static byte[] getNumRowkey(String rowkey, int i) {  
  181.         byte[] result = null;  
  182.   
  183.         RowKeyGenerator rkGen = new HashRowKeyGenerator();  
  184.         byte[] splitKeys = rkGen.nextId();  
  185.   
  186.         byte[] rowkeytmp = rowkey.getBytes();  
  187.           
  188.         byte[] intVal = BitUtils.getByteByInt(i); 

  189.        //hash值字符串+旧rowkey+参数i字符串=新rowkey 
  190.         result = new byte[splitKeys.length + rowkeytmp.length + intVal.length];  
  191.         System.arraycopy(splitKeys, 0, result, 0, splitKeys.length);  
  192.         System.arraycopy(rowkeytmp, 0, result, splitKeys.length,  
  193.                 rowkeytmp.length);  
  194.         System.arraycopy(intVal, 0, result, splitKeys.length+rowkeytmp.length,  
  195.                 intVal.length);  
  196.   
  197.         return result;  
  198.     }  
  199.       
  200.       
  201.   
  202.     /** 
  203.      * 删除表 
  204.      *  
  205.      * @param tableName 
  206.      */  
  207.     public static void dropTable(String tableName) {  
  208.         try {  
  209.             HBaseAdmin admin = new HBaseAdmin(config);  
  210.             admin.disableTable(tableName);  
  211.             admin.deleteTable(tableName);  
  212.         } catch (MasterNotRunningException e) {  
  213.             e.printStackTrace();  
  214.         } catch (ZooKeeperConnectionException e) {  
  215.             e.printStackTrace();  
  216.         } catch (IOException e) {  
  217.             e.printStackTrace();  
  218.         }  
  219.     }  
  220.   
  221.     /** 
  222.      * 查询所有 
  223.      *  
  224.      * @param tableName 
  225.      */  
  226.     public static void QueryAll(String tableName) {  
  227.         HTable table  = null;  
  228.         try {  
  229.             table  = new HTable(config, tableName);  
  230.             ResultScanner rs = table.getScanner(new Scan());  
  231.             for (Result r : rs) {  
  232.                 System.out.println("获得到rowkey:" + new String(r.getRow()));  
  233.                 for (KeyValue keyValue : r.raw()) {  
  234.                     System.out.println("列:" + new String(keyValue.getFamily())  
  235.                             + "====值:" + new String(keyValue.getValue()));  
  236.                 }  
  237.             }  
  238.         } catch (IOException e) {  
  239.             e.printStackTrace();  
  240.         }  
  241.     }  
  242.   
  243.     /** 
  244.      * 查询所有 
  245.      *  
  246.      * @param tableName 
  247.      */  
  248.     public static void QueryByCondition1(String tableName) {  
  249.   
  250.         HTable table = null;  
  251.         try {  
  252.             table  = new HTable(config, tableName);  
  253.             Get scan = new Get("abcdef".getBytes());// 根据rowkey查询  
  254.             Result r = table.get(scan);  
  255.             System.out.println("获得到rowkey:" + new String(r.getRow()));  
  256.             for (KeyValue keyValue : r.raw()) {  
  257.                 System.out.println("列:" + new String(keyValue.getFamily())  
  258.                         + "====值:" + new String(keyValue.getValue()));  
  259.             }  
  260.         } catch (IOException e) {  
  261.             e.printStackTrace();  
  262.         }  
  263.     }  
  264.       
  265.     /** 
  266.      *  根据rowkwy前坠查询  
  267.      * @param tableName 
  268.      * @param rowkey 
  269.      */  
  270.     public static void queryByRowKey(String tableName,String rowkey)  
  271.     {  
  272.         try {  
  273.             HTable table = new HTable(config, tableName);  
  274.             Scan scan = new Scan();  
  275.             scan.setFilter(new PrefixFilter(rowkey.getBytes()));  
  276.             ResultScanner rs = table.getScanner(scan);  
  277.             KeyValue[] kvs = null;  
  278.             for (Result tmp : rs)  
  279.             {  
  280.                 kvs = tmp.raw();  
  281.                 for (KeyValue kv : kvs)  
  282.                 {  
  283.                     System.out.print(kv.getRow()+" ");  
  284.                     System.out.print(kv.getFamily()+" :");  
  285.                     System.out.print(kv.getQualifier()+" ");  
  286.                     System.out.print(kv.getTimestamp()+" ");  
  287.                     System.out.println(kv.getValue());  
  288.                 }  
  289.             }  
  290.         } catch (IOException e) {  
  291.             e.printStackTrace();  
  292.         }  
  293.           
  294.     }  
  295.     /** 
  296.      * 查询所有 
  297.      *  
  298.      * @param tableName 
  299.      */  
  300.     public static void QueryByCondition2(String tableName) {  
  301.   
  302.         try {  
  303.             HTable table = new HTable(config, tableName);  
  304.             // 当列column1的值为aaa时进行查询  
  305.             Filter filter = new SingleColumnValueFilter(  
  306.                     Bytes.toBytes("column1"), null, CompareOp.EQUAL,  
  307.                     Bytes.toBytes("aaa"));   
  308.             Scan s = new Scan();  
  309.             s.setFilter(filter);  
  310.             ResultScanner rs = table.getScanner(s);  
  311.             for (Result r : rs) {  
  312.                 System.out.println("获得到rowkey:" + new String(r.getRow()));  
  313.                 for (KeyValue keyValue : r.raw()) {  
  314.                     System.out.println("列:" + new String(keyValue.getFamily())  
  315.                             + "====值:" + new String(keyValue.getValue()));  
  316.                 }  
  317.             }  
  318.         } catch (Exception e) {  
  319.             e.printStackTrace();  
  320.         }  
  321.   
  322.     }  
  323.   
  324.     /** 
  325.      * 查询所有 
  326.      *  
  327.      * @param tableName 
  328.      */  
  329.     public static void QueryByCondition3(String tableName) {  
  330.   
  331.         try {  
  332.               
  333.             HTable table = new HTable(config, tableName);  
  334.   
  335.             List<Filter> filters = new ArrayList<Filter>();  
  336.   
  337.             Filter filter1 = new SingleColumnValueFilter(  
  338.                     Bytes.toBytes("column1"), null, CompareOp.EQUAL,  
  339.                     Bytes.toBytes("aaa"));  
  340.             filters.add(filter1);  
  341.   
  342.             Filter filter2 = new SingleColumnValueFilter(  
  343.                     Bytes.toBytes("column2"), null, CompareOp.EQUAL,  
  344.                     Bytes.toBytes("bbb"));  
  345.             filters.add(filter2);  
  346.   
  347.             Filter filter3 = new SingleColumnValueFilter(  
  348.                     Bytes.toBytes("column3"), null, CompareOp.EQUAL,  
  349.                     Bytes.toBytes("ccc"));  
  350.             filters.add(filter3);  
  351.   
  352.             FilterList filterList1 = new FilterList(filters);  
  353.   
  354.             Scan scan = new Scan();  
  355.             scan.setFilter(filterList1);  
  356.             ResultScanner rs = table.getScanner(scan);  
  357.             for (Result r : rs) {  
  358.                 System.out.println("获得到rowkey:" + new String(r.getRow()));  
  359.                 for (KeyValue keyValue : r.raw()) {  
  360.                     System.out.println("列:" + new String(keyValue.getFamily())  
  361.                             + "====值:" + new String(keyValue.getValue()));  
  362.                 }  
  363.             }  
  364.             rs.close();  
  365.   
  366.         } catch (Exception e) {  
  367.             e.printStackTrace();  
  368.         }  
  369.   
  370.     }  
  371. }

      

  1. HashChoreWoker:  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.TreeSet;  
  5.   
  6. import org.apache.hadoop.hbase.util.Bytes;  
  7.   
  8. /** 
  9.  *  
  10.  * @author kuang hj 
  11.  * 
  12.  */  
  13. public class HashChoreWoker{  
  14.     // 随机取机数目  
  15.     private int baseRecord;  
  16.     // rowkey生成器  
  17.     private RowKeyGenerator rkGen;  
  18.     // 取样时,由取样数目及region数相除所得的数量.  
  19.     private int splitKeysBase;  
  20.     // splitkeys个数  
  21.     private int splitKeysNumber;  
  22.     // 由抽样计算出来的splitkeys结果  
  23.     private byte[][] splitKeys;  
  24.   
  25.     public HashChoreWoker(int baseRecord, int prepareRegions) {  
  26.         this.baseRecord = baseRecord;  
  27.         // 实例化rowkey生成器  
  28.         rkGen = new HashRowKeyGenerator();  
  29.         splitKeysNumber = prepareRegions - 1;  
  30.         splitKeysBase = baseRecord / prepareRegions;  
  31.     }  
  32.   
  33.     public byte[][] calcSplitKeys() {  
  34.         splitKeys = new byte[splitKeysNumber][];  
  35.         // 使用treeset保存抽样数据,已排序过  
  36.         TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);  
  37.         for (int i = 0; i < baseRecord; i++) {  
  38.             rows.add(rkGen.nextId());  
  39.         }  
  40.         int pointer = 0;  
  41.         Iterator<byte[]> rowKeyIter = rows.iterator();  
  42.         int index = 0;  
  43.         while (rowKeyIter.hasNext()) {  
  44.             byte[] tempRow = rowKeyIter.next();  
  45.             rowKeyIter.remove();  
  46.             if ((pointer != 0) && (pointer % splitKeysBase == 0)) {  
  47.                 if (index < splitKeysNumber) {  
  48.                     splitKeys[index] = tempRow;  
  49.                     index++;  
  50.                 }  
  51.             }  
  52.             pointer++;  
  53.         }  
  54.         rows.clear();  
  55.         rows = null;  
  56.         return splitKeys;  
  57.     }  
  58. }

  1. HashRowKeyGenerator:  
  2. import org.apache.hadoop.hbase.util.Bytes;  
  3. import org.apache.hadoop.hbase.util.MD5Hash;  
  4.   
  5. import com.kktest.hbase.BitUtils;  
  6. /** 
  7. * 
  8. * 
  9. **/  
  10. public class HashRowKeyGenerator implements RowKeyGenerator {  
  11.     private static long currentId = 1;  
  12.     private static long currentTime = System.currentTimeMillis();  
  13.     //private static Random random = new Random();  
  14.   
  15.     public byte[] nextId()   
  16.     {  
  17.         try {  
  18.             currentTime = getRowKeyResult(Long.MAX_VALUE - currentTime);  
  19.             byte[] lowT = Bytes.copy(Bytes.toBytes(currentTime), 44);  
  20.             byte[] lowU = Bytes.copy(Bytes.toBytes(currentId), 44);  
  21.             byte[] result = Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowT, lowU))  
  22.                     .substring(08).getBytes(), Bytes.toBytes(currentId));  
  23.             return result;  
  24.         } finally {  
  25.             currentId++;  
  26.         }  
  27.     }  
  28.       
  29.     /** 
  30.      *  getRowKeyResult 
  31.      * @param tmpData 
  32.      * @return 
  33.      */  
  34.     public static long getRowKeyResult(long tmpData)  
  35.     {  
  36.         String str = String.valueOf(tmpData);  
  37.         StringBuffer sb = new StringBuffer();  
  38.         char[] charStr = str.toCharArray();  
  39.         for (int i = charStr.length -1 ; i > 0; i--)  
  40.         {  
  41.             sb.append(charStr[i]);  
  42.         }  
  43.           
  44.         return Long.parseLong(sb.toString());  
  45.     }  
  46. }  


  上述代码示例通过getNewRowkey和getNumRowkey产生新的rowkey,即在rowkey前添加hash值,解决了rowkey热点问题。

   当然该示例还包括了put,get,scan等操作。


顺便提一下rowkey设计注意点:

1.避免rowkey热点,通过hash、翻转rowkey、组合rowkey等方法可以避免这个问题,尽量避免直接使用time作为rowkey。

2.充分利用rowkey有序的特点,key-value对在hbase中的存储,是按照key来进行排序的。

3.使用多个字段组合成rowkey。


参考文章:http://blog.csdn.net/kuanghongjiang/article/details/41343789

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值