在HBase中我们都知道rowkey的设计尤为重要,其设计原则分为三种:

  • 长度原则
  • 散列原则
  • 唯一原则

那么下面对rowkey的设计做一个详细的阐述:

HBase的rowkey设计可以说是使用HBase最为重要的事情,直接影响到HBase的性能,常见的RowKey的设计问题及对应访问为:

Hotspotting

的行由行键按字典顺序排序,这样的设计优化了扫描,允许存储相关的行或者那些将被一起读的邻近的行。然而,设计不好的行键是导致 hotspotting 的常见原因。当大量的客户端流量( traffic )被定向在集群上的一个或几个节点时,就会发生 hotspotting。这些流量可能代表着读、写或其他操作。流量超过了承载该region的单个机器所能负荷的量,这就会导致性能下降并有可能造成region的不可用。在同一 RegionServer 上的其他region也可能会受到其不良影响,因为主机无法提供服务所请求的负载。设计使集群能被充分均匀地使用的数据访问模式是至关重要的。

为了防止在写操作时出现 hotspotting ,设计行键时应该使得数据尽量同时往多个region上写,而避免只向一个region写,除非那些行真的有必要写在一个region里。

下面介绍了集中常用的避免 hotspotting 的技巧,它们各有优劣:

Salting

Salting 从某种程度上看与加密无关,它指的是将随机数放在行键的起始处。进一步说,salting给每一行键随机指定了一个前缀来让它与其他行键有着不同的排序。所有可能前缀的数量对应于要分散数据的region的数量。如果有几个“hot”的行键模式,而这些模式在其他更均匀分布的行里反复出现,salting就能到帮助。下面的例子说明了salting能在多个RegionServer间分散负载,同时也说明了它在读操作时候的负面影响。

假设行键的列表如下,表按照每个字母对应一个region来分割。前缀‘a’是一个region,‘b’就是另一个region。在这张表中,所有以‘f’开头的行都属于同一个region。这个例子关注的行和键如下:

foo0001foo0002foo0003foo0004

现在,假设想将它们分散到不同的region上,就需要用到四种不同的 salts :a,b,c,d。在这种情况下,每种字母前缀都对应着不同的一个region。用上这些salts后,便有了下面这样的行键。由于现在想把它们分到四个独立的区域,理论上吞吐量会是之前写到同一region的情况的吞吐量的四倍。

a-foo0003b-foo0001c-foo0004d-foo0002

如果想新增一行,新增的一行会被随机指定四个可能的salt值中的一个,并放在某条已存在的行的旁边。

a-foo0003b-foo0001c-foo0003c-foo0004d-foo0002

由于前缀的指派是随机的,因而如果想要按照字典顺序找到这些行,则需要做更多的工作。从这个角度上看,salting增加了写操作的吞吐量,却也增大了读操作的开销。

Hashing

可用一个单向的 hash 散列来取代随机指派前缀。这样能使一个给定的行在“salted”时有相同的前缀,从某种程度上说,这在分散了RegionServer间的负载的同时,也允许在读操作时能够预测。确定性hash( deterministic hash )能让客户端重建完整的行键,以及像正常的一样用Get操作重新获得想要的行。

考虑和上述salting一样的情景,现在可以用单向hash来得到行键foo0003,并可预测得‘a’这个前缀。然后为了重新获得这一行,需要先知道它的键。可以进一步优化这一方法,如使得将特定的键对总是在相同的region。

Reversing the Key(反转键)

第三种预防hotspotting的方法是反转一段固定长度或者可数的键,来让最常改变的部分(最低显著位, the least significant digit )在第一位,这样有效地打乱了行键,但是却牺牲了行排序的属性

单调递增行键/时序数据

在一个集群中,一个导入数据的进程锁住不动,所有的client都在等待一个region(因而也就是一个单个节点),过了一会后,变成了下一个region… 如果使用了单调递增或者时序的key便会造成这样的问题。使用了顺序的key会将本没有顺序的数据变得有顺序,把负载压在一台机器上。所以要尽量避免时间戳或者序列(e.g. 1, 2, 3)这样的行键。

如果需要导入时间顺序的文件(如log)到HBase中,可以学习OpenTSDB的做法。它有一个页面来描述它的HBase模式。OpenTSDB的Key的格式是[metric_type][event_timestamp],乍一看,这似乎违背了不能将timestamp做key的建议,但是它并没有将timestamp作为key的一个关键位置,有成百上千的metric_type就足够将压力分散到各个region了。因此,尽管有着连续的数据输入流,Put操作依旧能被分散在表中的各个region中

简化行和列

在HBase中,值是作为一个单元(Cell)保存在系统的中的,要定位一个单元,需要行,列名和时间戳。通常情况下,如果行和列的名字要是太大(甚至比value的大小还要大)的话,可能会遇到一些有趣的情况。在HBase的存储文件( storefiles )中,有一个索引用来方便值的随机访问,但是访问一个单元的坐标要是太大的话,会占用很大的内存,这个索引会被用尽。要想解决这个问题,可以设置一个更大的块大小,也可以使用更小的行和列名 。压缩也能得到更大指数。

大部分时候,细微的低效不会影响很大。但不幸的是,在这里却不能忽略。无论是列族、属性和行键都会在数据中重复上亿次。

列族

尽量使列族名小,最好一个字符。(如:f 表示)

属性

详细属性名 (如:”myVeryImportantAttribute”) 易读,最好还是用短属性名 (e.g., “via”) 保存到HBase.

行键长度

让行键短到可读即可,这样对获取数据有帮助(e.g., Get vs. Scan)。短键对访问数据无用,并不比长键对get/scan更好。设计行键需要权衡

字节模式

long类型有8字节。8字节内可以保存无符号数字到18,446,744,073,709,551,615。如果用字符串保存——假设一个字节一个字符——需要将近3倍的字节数。

下面是示例代码,可以自己运行一下:​​​​​​​

long l = 1234567890L;byte[] lb = Bytes.toBytes(l);System.out.println("long bytes length: " + lb.length);
String s = String.valueOf(l);byte[] sb = Bytes.toBytes(s);System.out.println("long as string length: " + sb.length);
MessageDigest md = MessageDigest.getInstance("MD5");byte[] digest = md.digest(Bytes.toBytes(s));System.out.println("md5 digest bytes length: " + digest.length);String sDigest = new String(digest);byte[] sbDigest = Bytes.toBytes(sDigest);System.out.println("md5 digest as string length: " + sbDigest.length);

不幸的是,用二进制表示会使数据在代码之外难以阅读。下例便是当需要增加一个值时会看到的shell:​​​​​​​

hbase(main):001:0> incr 't', 'r', 'f:q', 1COUNTER VALUE = 1
hbase(main):002:0> get 't', 'r'COLUMN                                        CELL f:q                                          timestamp=1369163040570, value=\x00\x00\x00\x00\x00\x00\x00\x011 row(s) in 0.0310 seconds

这个shell尽力在打印一个字符串,但在这种情况下,它决定只将进制打印出来。当在region名内行键会发生相同的情况。如果知道储存的是什么,那自是没问题,但当任意数据都可能被放到相同单元的时候,这将会变得难以阅读。这是最需要权衡之处。

倒序时间戳

一个数据库处理的通常问题是找到最近版本的值。采用倒序时间戳作为键的一部分可以对此特定情况有很大帮助。该技术包含追加( Long.MAX_VALUE - timestamp ) 到key的后面,如 [key][reverse_timestamp] 。

表内[key]的最近的值可以用[key]进行Scan,找到并获取第一个记录。由于HBase行键是排序的,该键排在任何比它老的行键的前面,所以是第一个。

该技术可以用于代替版本数,其目的是保存所有版本到“永远”(或一段很长时间) 。同时,采用同样的Scan技术,可以很快获取其他版本。

行键和列族

行键在列族范围内。所以同样的行键可以在同一个表的每个列族中存在而不会冲突。

行键不可改

行键不能改变。唯一可以“改变”的方式是删除然后再插入。这是一个常问问题,所以要注意开始就要让行键正确(且/或在插入很多数据之前)。

行键和region split的关系

如果已经 pre-split (预裂)了表,接下来关键要了解行键是如何在region边界分布的。为了说明为什么这很重要,可考虑用可显示的16位字符作为键的关键位置(e.g., “0000000000000000” to “ffffffffffffffff”)这个例子。通过 Bytes.split来分割键的范围(这是当用 Admin.createTable(byte[] startKey, byte[] endKey, numRegions) 创建region时的一种拆分手段),这样会分得10个region。​​​​​​​

48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48                                // 054 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10                 // 661 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -68                 // =68 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -126  // D75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 72                                // K82 18 18 18 18 18 18 18 18 18 18 18 18 18 18 14                                // R88 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -44                 // X95 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -102                // _102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 102                // f

但问题在于,数据将会堆放在前两个region以及最后一个region,这样就会导致某几个region由于数据分布不均匀而特别忙。为了理解其中缘由,需要考虑ASCII Table的结构。根据ASCII表,“0”是第48号,“f”是102号;但58到96号是个巨大的间隙,考虑到在这里仅[0-9]和[a-f]这些值是有意义的,因而这个区间里的值不会出现在键空间( keyspace ),进而中间区域的region将永远不会用到。为了pre-split这个例子中的键空间,需要自定义拆分。

教程1:预裂表( pre-splitting tables ) 是个很好的实践,但pre-split时要注意使得所有的region都能在键空间中找到对应。尽管例子中解决的问题是关于16位键的键空间,但其他任何空间也是同样的道理。

教程2:16位键(通常用到可显示的数据中)尽管通常不可取,但只要所有的region都能在键空间找到对应,它依旧能和预裂表配合使用。

一下case说明了如何16位键预分区

public static boolean createTable(Admin admin, HTableDescriptor table, byte[][] splits)throws IOException {  try {    admin.createTable( table, splits );    return true;  } catch (TableExistsException e) {    logger.info("table " + table.getNameAsString() + " already exists");        return false;  }}
public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) {  byte[][] splits = new byte[numRegions-1][];  BigInteger lowestKey = new BigInteger(startKey, 16);  BigInteger highestKey = new BigInteger(endKey, 16);  BigInteger range = highestKey.subtract(lowestKey);  BigInteger regionIncrement = range.divide(BigInteger.valueOf(numRegions));  lowestKey = lowestKey.add(regionIncrement);  for(int i=0; i < numRegions-1;i++) {    BigInteger key = lowestKey.add(regionIncrement.multiply(BigInteger.valueOf(i)));    byte[] b = String.format("%016x", key).getBytes();    splits[i] = b;  }  return splits;}

摘自:https://help.aliyun.com/document_detail/59035.html?spm=a2c4g.11186623.6.773.3c3e7bb29cWzA0

Rowkey设计_HBase表设计相关推荐

  1. 走向云计算之HBase模式设计及表设计案例

    一.概述 HBase有以下几个特点: HBase列的可以动态增加,并且列为空就不存储数据,节省存储空间. hbase自动切分数据,使得数据存储自动具有水平scalability. Hbase可以提供高 ...

  2. mysql栏目表设计_MySQL表设计

    文章摘自: https://mp.weixin.qq.com/s?__biz=MzI2NDU3OTg5Nw==&mid=2247483799&idx=1&sn=4d1f45ab ...

  3. 抽奖活动mysql表设计_购物商城数据库设计-商品表设计

    大家好,今天我们来设计一下购物商城的商品表. 我们的目标是表结构能够满足下面这张图的搜索: 在设计表之前,我们先来了解下商品中的两个概念:SPU和SKU SPU SPU(Standard Produc ...

  4. (二)购物商城数据库设计-商品表设计

    大家好,今天我们来设计一下购物商城的商品表. 我们的目标是表结构能够满足下面这张图的搜索: 在设计表之前,我们先来了解下商品中的两个概念:SPU和SKU SPU SPU(Standard Produc ...

  5. oracle数据库纵表设计,oracle 数据库设计-数据库表设计

    在数据库设计中,我的工作中经常会分析怎样商业逻辑中的表格如何设计.再设计表的关系之前 需要先了解关系型数据库特点 1关系型数据库,是指采用了关系模型来组织数据的数据库: 2.关系型数据库的最大特点就是 ...

  6. dj电商-数据表的设计-用户表设计

    数据库的设计是关键的一步 本节根据界面,一步一步的推导出表需要有的字段内容 注册页面 >需要有一个用户表 记录用户注册相关的信息 用户名,密码,邮箱 登陆页面 登陆页需的信息,前面设计了 用户中 ...

  7. 【MySQL】高性能高可用表设计实战-表设计篇(MySQL专栏启动)

  8. (四)MySQL学习笔记——多表设计、多表查询、多表查询练习题

    文章目录 一.多表设计 1.一对一设计 2.一对多设计 3.多对多设计 二.多表查询 多表查询前的数据准备 1.内连接查询 2.外连接查询 3.子查询 4.自关联查询 三.多表查询练习 一.多表设计 ...

  9. 商城SPU SKU 套餐表设计

    目录 SPU SKU表设计 套餐表设计 SPU SKU表设计 套餐表设计

最新文章

  1. Apache与Nginx比较(Nginx 高并发情况常用,必须学习)
  2. CentOS安装rpm包时遇到Header V3 DSA signature: NOKEY时解决办法
  3. Web Client Software Factory系列(4):数据绑定和ObjectContainerDataSource控件
  4. 1.7 什么时候该改变开发_测试集和指标
  5. Emacs 下安装 python-mode.el
  6. mysql db for python_Python使用MySQLdb for Python操作数据库教程
  7. sql学生选课管理系统
  8. oracle查看密码过期策略及修改
  9. 请设计输出实数的格式,包括:⑴一行输出一个实数;⑵一行内输出两个实数;⑶一行内输出三个实数。实数用“6.2f“格式输出。
  10. android mediastore指定前置摄像头,Intent方式打开前置摄像头
  11. Java中浏览量怎么实现_Java刷视频浏览量点赞量的实现代码
  12. layUI table表格合并
  13. Ubuntu和本机之间复制粘贴
  14. 迅雷极速版修改边下边播的默认播放器为PotPlayer
  15. u盘win7纯净版_教你安装纯净版windows系统
  16. 对产品经理来说,PMP和NPDP两个证书,哪一个权威性比较大?
  17. JS禁用页面某区域不可选中文字、及修复导致行内编辑出现某单元格不可输入的问题
  18. 在船舶共轨实验平台上使用Mbed LPC1768
  19. 波士顿地区Airbnb价格预测Project (一)
  20. web期末网站设计大作业(中华传统文化主题学生网页设计源码)

热门文章

  1. 牛客多校6 - Harmony Pairs(数位dp)
  2. CodeForces - 1343F Restore the Permutation by Sorted Segments(思维)
  3. CodeForces - 1284B New Year and Ascent Sequence(乱搞)
  4. 深度学习-人工神经网络概述
  5. cometoj contest 6(记录型博客)
  6. 《openssl编程》之配置文件
  7. cocos2d-x游戏开发(十四)用shader使图片背景透明
  8. new A和new A()的区别详解
  9. Redis 持久化策略 : RDB持久化、AOF持久化、混合持久化
  10. 老司机给我们解读 Spring Boot 最流行的 16 条实践