HBase的数据模型也是由表组成,每一张表里也有数据行和列,可是在HBase数据库中的行和列又和关系型数据库的稍有不一样。数据库
表(Table): HBase会将数据组织成一张表,表名必须是能用在文件路径里的合法名字,由于HBase的表是映射成hdfs上面的文件,Hbase的表及表中的数据都是以二进制字节进行存储;
行(Row): 在表里面,每一行都是以一个行键(Row Key)来进行惟一标识的,行键并无什么特定的数据类型,以二进制的字节来存储。
列族(Column Family): 列族是Hbase Schema定义的一部分,列不是,列族一旦肯定后,就不能轻易修改,由于它会影响到HBase真实的物理存储结构,可是列族中的列标识(Column Qualifier)以及其对应的值能够动态增删。表中的每一行都有相同的列族,可是不须要每一行的列族里都有一致的列标识(Column Qualifier)和值,因此说是一种稀疏的表结构,这样能够必定程度上避免数据的冗余;
列标识(Column Qualifier): 列族中的数据经过列标识来进行映射,即为一个键值对,Column Qualifier就是Key,值是value;
单元(Cell): 每个行键,列族和列标识共同组成一个单元,存储在单元里的数据称为单元数据;
时间戳(Timestamp): 默认下每个单元中的数据插入时都会用时间戳来进行版本标识。读取单元数据时,若是时间戳没有被指定,则默认返回最新的数据,写入新的单元数据时,若是没有设置时间戳,默认使用当前时间。每个列族的单元数据的版本数量都被HBase单独维护,默认状况下HBase保留3个版本数据。数组
Hbase必定程度上又能够当作一个多维度的Map模型去理解它的数据模型。即以行键(Row Key),列标识(column qualifier),时间戳(timestamp)标识的有序Map数据结构的数据库,具备稀疏,分布式,持久化,多维度等特色。正以下图,一个行键映射一个列族数组,列族数组中的每一个列族又映射一个列标识数组,列标识数组中的每个列标识(Column Qualifier)又映射到一个时间戳数组,里面是不一样时间戳映射下不一样版本的值,可是默认取最近时间的值,因此能够当作是列标识(Column Qualifier)和它所对应的值的映射。用户也能够经过HBase的API去同时获取到多个版本的单元数据的值。Row Key在HBase中也就至关于关系型数据库的主键,而且Row Key在建立表的时候就已经设置好,用户没法指定某个列做为Row Key。服务器
HBase也能够当作是一个相似Redis那样的Key-Value数据库。以下图,当你要查询某一行的全部数据时,Row Key就至关于Key,而Value就是单元中的数据(列族,列族里的列和列中时间戳所对应的不一样版本的值);用户要查询指定行里某一条单元数据时,HBase会去读取一个数据块,里面除了有要查询的单元数据,可能同时也会获取到其它单元数据,由于这个数据块还包含着这个Row Key所对应的其它列族或其它的列信息,这些信息实际也表明着另外一个单元数据,这也是HBase的API内部实际的工做原理。数据结构
HBase提供了丰富的API接口让用户去操做这些数据。主要的API接口有3个,Put,Get,Scan。Put和Get是操做指定行的数据的,因此须要提供行键来进行操做。Scan是操做必定范围内的数据,经过指定开始行键和结束行键来获取范围,若是没有指定开始行键和结束行键,则默认获取全部行数据。分布式
在HBase表设计中最重要的就是定义Row-Key的结构,要定义Row-Key的结构时要紧密结合实际业务应用场景,对这张表出现什么样的读写场景。除此以外,在设计表的时候咱们也应该要考虑HBase数据库的一些特性。
1. HBase中表的索引是经过Key来实现的
2. 在表中是经过Row Key的字典序来对一行行的数据来进行排序的,表中每一块区域的划分都是经过开始Row Key和结束Row Key来决定的。
3. 全部存储在HBase表中的数据都是二进制的字节,并无数据类型。
4. 原子性只在行内保证,HBase表中并无多行事务。
5. 列族(Column Family)在表建立以前就要定义好
6. 列族中的列标识(Column Qualifier)能够在表建立完之后动态插入数据时添加。性能
2、Hbase 表设计示例优化
设计一张表,用来保存微博上用户互粉的信息。加密
读场景业务要求
1. 每一个用户都关注了谁
2. 用户A有没有关注用户B
3. 谁关注了用户Aspa
写场景业务要求
1. 用户关注了另外一个用户
2. 用户取消关注某个用户.net
每一行表明着某个用户和全部他所关注的其它用户。这个用户ID做为Row Key,一个列族follows,而每个列标识(Column Qualifier)就是这个用户所关注的其余用户在列族里的序号,单元数据就是这个用户所关注的其余用户的用户ID。在这种表结构的设计下,“每一个用户都关注了谁”这个问题很好解决,但对于“用户A有没有关注用户B”这个问题在列不少的时候,须要遍历A用户所在Row的全部单元数据去找到用户B,这样的开销会十分大。而且当添加新的被关注用户时,由于不知道给这个新用户分配什么样的列族序号,须要遍历整个列族中的全部列找出最后一个列,并将最后一个列的序号+1给新的被关注用户做为列族内的序号,这样的开销也十分大。
在初始方案的基础上,添加一个counter记录列族中全部列的总数量,当添加新的被关注用户时,这个新用户的序号就是counter+1。可是当要取消关注某个用户时,同样得遍历全部的列数据,并且最大的问题是在于HBase不支持事务处理,这种经过counter来添加被关注用户的操做逻辑得写在客户端中。
列标识(Column Qualifier)存储的时候是二进制的字节,能够存储任何数据,并且列标识仍是动态增添的,基于这个特性咱们再改进表的设计,以下图。此次以被关注的用户ID作为列标识(Column Qualifier),而后单元数据能够是任意数字,好比所有统一成1。在这种表结构的设计下,添加新的被关注者,以及取消关注都会变得很简单。可是对于读场景中,谁关注了用户A这个问题,由于HBase数据库的索引只创建在Row Key上,这里不得不扫描全表去统计全部关注了用户A的用户数量。
将Row Key设计成“followerID+followedID”的形式,好比:“Jame+Emma”,这里的Row Key值就表明着Jame关注了Emma(其实这里应该是“Jame的ID+Emma的ID”,只是为了解释方便而直接用名字),同时包含了关注者和被关注者两个信息;还须要注意的一点就是列族的名字被设计成只有一个字母f,这样设计的好处就是减小了HBase对数据的I/O操做压力,同时减小了返回到客户端的数据字节,提升响应速度,由于每个返回给客户端的KeyValue对象都会包含列族名字。同时将被关注人的用户名称也保存在了表中做为Column Qualifier,这样作的好处就是节省了去用户表查找用户名的资源。在这种表结构设计下,“用户A取消关注某个用户B”,“用户A有没有关注用户B?”的业务处理就会变得简单高效。
在实际的生产环境中,还须要将Row Key使用MD5加密,一方面是使Row Key的长度都一致,能提升数据的存取性能。
http://0b4af6cdc2f0c5998459-c0245c5c937c5dedcca3f1764ecc9b2f.r43.cf2.rackcdn.com/9353-login1210_khurana.pdf
https://blog.csdn.net/ymh198816/article/details/51244911