以列作为存储单位进行数据存储,表现就是:一个列为一个存储单元,比如一个列就是一个文件
逻辑结构
1.在Hbase中没有主键的概念,取而代之的是行键,相当于关系型数据库中的主键比如id
2.不同于传统的关系型数据库,在HBase中,定义表的时候不需要指定行键列,而是在添加数据的时候来手动添加行键,只要说一下这是个行键列就可以了
3.HBase默认会对行键进行排序,按照字典序排序
列族(column family)是HBase中最基本的数据结构,它是一组列(column)的集合。列族中的列具有相同的前缀,列族可以理解为一种逻辑上的分组。列族在HBase中有以下几个重要的特点:
列族是HBase中数据存储的基本单位,一个表可以有多个列族。
列族内的列名是有序的,列名的前缀相同,即属于同一个列族。
列族在HBase中的存储结构是有序的,同一列族的数据会被存储在同一块磁盘空间上,这有助于提高读写性能。
列族在HBase中的存储格式是列式存储,即同一列族中的列可以不同时存在,这有助于节省存储空间。
HBase 的逻辑存储视图由行键、时间戳、列族组成一个类似二维表一样的结构,但是在实际存储的时候,数据库存储的数据是以列族为单位进行存储的,是完全将行数据按列族的方式进行分列
列族与列的关系
列族和列之间的关系是一种包含关系。列族包含了多个列,列具有唯一的列名和列值。在HBase中,列名是由列族名和具体的列名组成的。例如,如果有一个列族名为“user”,那么在这个列族下可以有多个列名,如“name”、“age”、“gender”等。
列族与行(row)的关系
列族和行之间的关系是一种多对一的关系。一个行可以包含多个列族,而一个列族可以包含多个行。在HBase中,行是数据的唯一标识,每个行都有一个唯一的行键(rowkey)。同一个行可以包含多个列族,而同一个列族可以包含多个行。
物理结构
1.在HBase中,没有表关联的概念,取而代之的是用列族来进行统计
2.在HBase中,一个表中至少包含1个列族,可以包含多个列族,理论上不限制列族的数量
3.在HBase中,强调列族,但是不强调列-在定义表的时候必须定义列族,但是列可以动态增删,一个列族可以包含0到多个列
HBase的列级别,分为2个级别
级别1:列族,HBase列式数据库的单元,物理意义上的列。一个列族有独立的数据文件,不同的列族在不同的文件内。
级别2:列(二级列),存在与一个列族的内部
在一个项目中,需要使用HBase保存多张表,这些表会按照业务域来划分
为了方便管理,不同的业务域以名称空间(namespace)来划分,这样管理起来会更加容易
类似于Hive中的数据库,不同的数据库下可以放不同类型的表
default名称空间是默认创建表的位置,hbase是专门存放系统表的名称空间(namespace、meta)
管理命名空间指令
在HBase中没有database的概念,取而代之的是namespace
在HBase启动的时候,自带了两个空间:default和hbase。hbase空间下方的是HBase的基本信息;在建表的时候如果不指定,则表默认放在default空间下
# 一、命名空间
# 1.创建一个命名空间
create_namespace 'hello'
# 2.查看命名空间
list_namespace
# 3.删除命名空间
drop_namespace 'hello'
# 4.查看某个具体的命名空间
describe_namespace 'hello'
# 5.在命名空间hello里创建一个名为MSG的表,该表名为a1
create 'hello:MSG','a1'
注:在删除命名空间时要确保空间里没有表,否则会报错
HBase表的列是由列族名、限定符以及列名组成的,其中“:”为限定符。创建HBase表不需要指定列,因为列是可变的,非常灵活。
记录每次操作数据的时间,通常记作数据的版本号。数据写到HBase的时候都会被记录一个时间戳,这个时间戳被我们当做一个版本。比如说,我们修改或者删除某一条的时候,本质上是往里边新增一条数据,新增了记录的版本。
hbase在建表的时候,一个列族可以指定一个versions,用以表示所存数据的版本数,默认该值为3,即保存最近的3个版本的数据。在每一个cell中有同一数据的多个版本,按时间倒序排序。我们可以在建表的时候指定versions,在放数据的时候以一个时间戳(一个long值)来表示该数据的版本号。取数据时可以取最新的数据,也可以取特定时间戳的数据,某段时间戳的数据以及全部数据
物理存储结构
在HBase中,每一个列(二级列)的具体数据,都会有时间戳的属性。
如图所示,name和addr两个二级列的具体数据,有各自的时间戳属性。
默认是数据写入HBase的时候,HBase自动做的记录。
HBase中的时间戳是一个64位的long类型数值,用于标识数据的版本。每当向HBase中插入或更新数据时,可以通过指定时间戳来标记数据的版本。
时间戳属性有啥用?
时间戳的属性,在于让HBase的数据可以支持多版本。
多版本的意思是:一个二级列的具体数据,可以有多个版本。
也就是,比如,我们假设一张表,创建的时候,允许5个版本:
列族1的二级列addr,被更新了5次,分别是:
当我们查询这个具体的二级列的数据,默认返回最新时间戳的那一个,也就是杭州。
如果在来一条数据更新进来,最老的北京将被删除。
二级列的具体数据,有时间戳这个属性,的最大作用就在于,维护多个版本的时候
来判断,谁是老数据,谁是新数据。
HBase的时间戳可以用于以下操作:
插入数据:当向HBase中插入数据时,可以为每条数据指定一个时间戳。如果不指定时间戳,HBase将使用当前时间作为默认时间戳。可以使用Put
类的setTimestamp()
方法来设置时间戳。
更新数据:当更新HBase中的数据时,可以使用相同的row key和列族,但不同的时间戳来插入新的数据版本。HBase会根据时间戳的大小自动选择合适的数据版本。
读取数据:当从HBase中读取数据时,可以使用时间戳来获取特定版本的数据。可以使用Get
类的setTimeStamp()
方法来设置要读取的时间戳。如果不设置时间戳,则默认读取最新的数据版本。
删除数据:可以使用时间戳来删除特定版本的数据。可以使用Delete
类的setTimeStamp()
方法来设置要删除的时间戳。如果不设置时间戳,则默认删除所有版本的数据。
HBase是列式数据库,物理上的存储就是列族对应的文件。
每一个列族对应的文件,我们称之为:HFile
HFile里面存储的是什么呢?
我们以上图的列族1为例,看看内部是如何做数据存储的。
如图,我们标记了Rowkey(1,2,3)这三条数据,在HFile中的具体组织形式。
可以看出,HFile本质上内部也是行存储组织。分别有5个列:
在HFile中的一条数据 称之为单元格(Cell)
根据行键、列族和列可以映射到一个对应的单元格,单元格是HBase存储数据的具体地址。
行式存储
传统的数据库是关系型的,且是按行来存储的。如下图:
其中只有张三把一行数据填满了,李四王五赵六的行都没有填满。
因为这里的行结构是固定的,每一行都一样,即使你不用,也必须空到那里,而不能没有。
列式存储
为了与传统的区别,新型数据库叫做非关系型数据库,是按列来存储的。如下图
我们可以看到原来张三的六列数据变成了现在的六行。原来的六列数据是在一行,所以共用一个主键(即张三)。现在变成了六行,每行都需要一个主键(不然不知道这行数据是谁的),所以原来的主键(即张三)重复了六次
由于原来的列变为了现在的行,如果有需要就加一行,没需要就不加,不会造成空间浪费。
行列对比
行式存储倾向于结构固定,列式存储倾向于结构弱化。 (行式存储相当于套餐,即使一个人来了也给你上八菜一汤,造成浪费;列式存储相等于自助餐,按需自取,人少了也不浪费)
行式存储一行数据只需一份主键,列式存储一行数据需要多份主键。
行式存储存的都是业务数据,列式存储除了业务数据外,还要存储列名。
行式存储更像一个Java Bean,所有字段都提前定义好,且不能改变;列式存储更像一个Map,不提前定义,随意往里添加key/value。
Hbase虽然弱化了结构,但并不等于放任不管。传统关系型数据库在插入数据前表结构(即所有列和列的数据类型)已经是严格确定的。 Hbase的表在放入数据前也有需要确定下来的东西,那就是Column Family(常译为列族/列簇)。
单词Family就是家庭的意思,所以列族就是列的家庭。那么列自然就是家庭成员了,通常家庭成员都有多个,所以一个列族包含多个列。
一个家庭的成员之间具有血缘关系,所以一个列族的多个列之间通常也具有某种关系,比如相似或同种类别。所以列族可以看作是某种分类(归类)。
一个非常常见的例子,去面试的时候,一般前台MM都会让填一张表,通常信息很多,每个公司又不尽相同。但大致可以分三类:人员基本信息,教育经历信息,工作经历信息,这三个类别其实就相当于三个列族。如下图:
每个类别里都会有具体的信息,比如人员基本信息里有姓名、电话、出生年月等,它们就相当于一个个标识符(变量名),在Hbase中叫做Column Qualifier(列修饰符)。列修饰符位于列族里面用来标识一条条数据。如下图:
在Hbase中一个列族(Column Family)和一个列修饰符(Column Qualifier)组合起来才叫一个列(Column),使用冒号(:)分割,列族:列修饰符,如下图:
在传统数据库中每一行的唯一标识符叫做主键,在Hbase中叫做row key(行键)。如下图:
数据在进入Hbase时都会被打上一个时间戳,这个时间戳可以作为版本号来使用。
在t1时间我存入一个人的基本信息,之后发现姓名错了,在t2时间又更新了姓名,此时并不会去更新原来的那条数据,而是又插入了一条新数据且打上新的时间戳。
此时去查询获取的是新数据,仿佛是更新了,但其实只是默认返回了最新版本的数据而已。如下图:
一个行键、列族、列修饰符、数据和时间戳组合起来叫做一个单元格(Cell)。
这里的行键、列族、列修饰符和时间戳其实可以看作是定位属性(类似坐标),最终确定了一个数据。
下图中的一行相等于Hbase中的一个单元格:
一个行键、一到多列(包括数据)组合起来叫做一行(Row)。下图中所有1001的数据合起来相当于Hbase中的一行,1002的相当于另一行:
在Hbase中,只要确定了列族(具体的列不用管),表(Table)就确定了。如下图:
表的设计
列蔟:推荐1-2个,能使用1个就不是使用2个
版本的设计:如果我们的项目不需要保存历史的版本,直接按照默认配置VERSIONS=1就OK。如果项目中需要保存历史的变更信息,就可以将VERSIONS设置为>1。但是设置为大于1也就意味着要占用更多的空间
数据的压缩:在创建表的时候,可以针对列蔟指定数据压缩方式(GZ、SNAPPY、LZO)。GZ方式是压缩比最高的,13%左右的空间,但是它的压缩和解压缩速度慢一些
HBase的KeyValue型存储
KeyValue型(后续简称KV型),在Python中已经使用过。
就是Python的字典。
Python的字典,就是一个键值对(KeyValue)型的数据
d = {"name": "王大大", "age": 11, "addr": "北京"}
上述字典:name、age、addr都是key,王大大、11、北京都是对应key的value
所以
d['name'] 就可以取到王大大,通过key,取value(值)
d = {"info":{"name":"王大大", "age":11}, "score": [11,22,33]}
所以,info和score就是字典的key
通过d['info']就能取到value,也就是一个嵌套的字典
通过d['score']就能取到value,也就是一个嵌套的list
HBase就是一个KV型数据库,它有自己的key对应存储的value值。