分布式存储—— HBase数据模型 详解
目录
1.3 HBase数据模型
1.3.1 两类数据模型
1.3.2 数据模型的重要概念
1.3.3 数据模型的操作
1.3.4 数据模型的特殊属性
1.3.5 CAP原理与最终一致性
1.3.6 小结
本文章参考、总结于学校教材课本《HBase开发与应用》
1.3 HBase数据模型
在开始学习HBase之前非常有必要先学习HBase的特性,因此本节将介绍HBase的逻辑模型、物理模型和访问HBase的方法等。和传统的关系型数据库类似,HBase以表(Table)的方式组织数据,应用程序将数据存入HBase的表中。HBase的表由行(row)和列(Column)共同构成,与关系型数据库不同的是HBase有一个列族(Column Family)的概念,它将一列或者多列组织在一起,HBase的列必须属于某一个列族。
行和列的交叉点称为单元格(Cell), 单元格是版本化的。单元格的内容也就是列的值是不可分割的字节数组,以二进制形式存储。HBase没有数据类型,任何列值都被转换成字节数组进行存储。HBase表中的行是通过行键(Rowkey)进行区分的,行键也是用来唯一确定一行的标识,不同的行键代表不同的行,行键也是一段字节数组,不论是字符串还是数字,最终都会被转换成字节数组进行存储。HBase表中的行是按Rowkey排序的,排序方式采用字典顺序,所有表中的行都必须要有Rowkey。同时HBase是一种面向列的分布式的数据库,其物理模型和逻辑模型与传统的关系型数据库有很大的不同。下面我们将详细讲述HBase数据模型中的一些重要概念。
1.3.1 两类数据模型
本节将从逻辑模型和物理模型两方面来了解HBase的数据模型,表是HBase表达数据的逻辑组织方式,而基于列的存储则是数据在底层的组织方式。本节将首先学习关于逻辑模型的一些重要概念及基本操作以及HBase实际存储数据的一些特点,为后面的学习打好基础。
1.3.1.1 逻辑模型
HBase是一个类似GoogleBigTable的开源分布式数据库,大部分特性和BigTable相同,可以理解为是一个稀疏的、长期存储的、多维度的和排序的映射表,表中的每一行可以有不同的列。与关系型数据库不同,关系型数据库要求表在被创建时明确定义列以及列的数据类型,而HBase的同一个表的记录可以有不一样的列。
HBase中最基本的一单位是列,一列或者多列构成了行,行有行键(Rowkey),每一行的行键都是唯一的,相同行键的插入操作被认为是对同一行的操作,也就是说如果做了两次写入操作,而行键是同一个,那么后面的操作可以认为是对该行的某些列的更新操作。
HBase中的一个表有若干行,每行有很多列,列中的值有多个版本,每个版本的值称为一个单元格,每个单元存储的是不同时刻该列的值。图 1.3.1是Google的Big Table论文中的webtable表的逻辑模型,由于HBase被认为是BigTable的开源实现,所以该图对HBase完全适用,表名为Webtable,包含两个列族:contents和anchor。在该实例中,列族anchor:有两个列(anchor:cssnsi.com和anchor:my.look.ca),列族contents仅有一个列contents:html。
其中,列名是由列族前缀和修饰符(Quallfier)连接而成,分隔符是英文冒号。例如,列anchor:my.look.ca是列族anchor前缀和修饰符my.look.ca组成。所以在提到HBase的列的时候应该用“列族前缀+修饰符”的方式才准确。
如图 1.3.1所示,在表Webtable的逻辑模型中,所有的列族和列都紧凑在一起,其中并没有附带物理存储方式的概念。该逻辑视图是为了使读者更好地、更直观地理解HBase的数据模型,并不代表实际的数据存储也是这种格式。
图 1.3.1 Webtable的逻辑模型
如果熟悉java语言里的Map数据结构,可以把HBase理解为这种结构的无限嵌套版本。
1.3.1.2 物理模型
虽然在逻辑模型中,表可以被看成一个稀疏的行的集合。但在物理上,表是按列分开存储的。HBase的列是按列族分组的,HFile是面向列的,存放行的不同列的物理文件,一个列族的数据存放在多个HFile中,最重要的是一个列族的数据会被同一个Region管理,物理上存放在一起。Region是管理HFile的一种机制,这个将会在后面讨论。这种物理上存储的不同可以从下面的物理视图中直观看出,如表 1.3.1和表 1.3.2所示。表 1.3.1中展示了列族anchor的集中存储,表 1.3.2中展示了列族contents的集中存储。
表 1.3.1 列族anchor存储模型
行键 | 时间戳 | 列族和单元格值 |
com.cnn.www | t9 | anchor:cnnsi.com=”CNN” |
com.cnn.www | t8 | anchor:my.look.ca=”CNN.com” |
表 1.3.2 列族contents存储模型
行键 | 时间戳 | 列族和单元格值 |
com.cnn.www | t6 | contents:html=”<html>…” |
com.cnn.www | t5 | contente:html=”<html>…” |
com.cnn.www | t3 | contente:html=”<html>…” |
HBase的表被设计成可以不禁用表而随时加入新的列,因此可以将新列直接加入一个列族而无须声明。
1.3.2 数据模型的重要概念
HBase是一种列式存储的分布式数据库,其核心概念是表(Table)。与传统的关系型数据库一样,表由行和列组成,但HBase同一列可以存储不同时刻的值,同时多个列可以组成一个列族(Column Family),这种组织形式是出于存取性能考虑的。理解HBase的这些概念是很重要的,因为数据模型设计的好坏将直接影响你的查询性能。本节我们将讨论最重要的概念。
1.3.2.1 表
在HBase中数据以表的形式存储。使用表的主要原因是把某些列组织起来一起访问,同一个表中的数据通常是相关的,通过列族进一步把一些列组织在一起进行访问。用户可以通过命令行或Java API来创建表。表名通常使用Java string类型或byte[](二进制数组)表示,表名作为HDFS存储路径的一部分来使用,因此必须要符合文件名规范,所以构成表名的字符是有限制的。可以直接查看底层存储系统,在HDFS中可以看到每个表的表名都作为独立的目录结构,在某些情况下,用户可能需要查看这部分信息。
HBase列式存储格式允许用户存储大量的信息到相同的表中,而在RDBMS模型中,大量信息则需要切分成多个表存储。通常的数据库规范规则不适用于HBase,因此HBase中表的数量相对较少。
虽然理论上HBase的表是由行和列组成的,但是从物理结构上看,表存储在不同的分区,即不同的Region。每个Region只在一个RegionServer中提供服务,而Region直接向客户端提供存储和读取服务。
与传统的关系型数据库一样,HBase提供了命令行创建表,创建表时只需要指定表名和至少一个列族。列族影响表的物理存储结构,创建表后列族还可以更改,但是比较麻烦。这就是HBase表中的全部,和传统关系型数据库不同,HBase的表没有列定义,没有类型,这就是HBase被称为无模式数据库的原因。
如何与HBase建立连接呢?可以使用Shell,或者通过Java API,与使用JDBC或ODBC访问关系型数据库不同,访问HBase不需要用户名和密码,没有Schema;将HBase-site.xml配置文件复制一份到自己的工程中,HBase API会读取配置文件完成对HBase的连接。创建连接是一项非常消耗资源的工作,HBase为我们提供了一个连接池,可以更好地管理资源重用。
1.3.2.2 行键
行键,即Rowkey,是HBase中最为重要的概念之一,在本书1.4.2节会详细讲解关于行键的设计原则。行键是不可分割的字节数组。行键是按字典排序由低到高存储在表中的,以一个空的数组来标识表空间的起始或者结尾。图 1.3.2展示了行键的排列规则。
图 1.3.2 行键按照字典进行排序
这可能和你预期的顺序不太一样,在字典序中,数据按照二进制字节从左至右逐一对比形成最终的次序。例如row-1小于row-2,无论后面如何,row-1都排在row-2之前。
在HBase中行键是唯一的索引,不过在新版本的HBase中考虑了对辅助索引的支持。
为了高效检索数据,应该仔细设计Rowkey以获得最高的查询性能:首先Rowkey被冗余存储,所以长度不宜过长,过长的Rowkey将会占用大量的空间同时会降低检索效率;其次Rowkey应该尽量分布均匀,这样不会产生热点现象;最后是Rowkey唯一原则,必须在设计上保证其唯一性。
在1.4.2节将学习Rowkey的设计,针对不同的场景Rowkey需要有不同的设计以提高检索的效率。
1.3.2.3 列族
HBase中的列族是一些列的集合。一个列族中所有列成员有着相同的前缀。比如,列courses:history和courses:math都是列族courses的成员。冒号(:)是列族的分隔符,用来区分列族前缀和列名。Column前缀必须是可打印的字符,剩下的部分(称为Column Qualifier),可以由任意字节数组组成。
在物理上,一个列族的成员在文件系统上都是存储在一起的。因为存储优化都是针对列族级别的,这就意味着,一个列族的所有成员是通过相同的方式访问的。
在创建表的时候至少要指定一个列族,新的列族可以随后按需、动态地加入,但是修改列族要先停用表。应该将经常一起查询的列放在一个列族中,合理划分列族将减少查询时加载到缓存的数据,提高查询的效率,但也不要有太多的列族,因为跨列族访问是非常低效的。
1.3.2.4 单元格
HBase中的一单元格由行键、列族、列、时间戳唯一确定。一单元格的内容是不可分割的字节数组。每个单元格都保存着同一份数据的多个版本,不同时间版本的数据按照时间顺序倒序排序,最新时间的数据排在最前面,时间戳是64位的整数,可以由客户端在写入据时赋值,也可以由RegionServer自动赋值。
1.3.3 数据模型的操作
HBase对数据模型的4个主要操作包括Get、Put、Scan和Delete。通过HTable实例进行操作,用户可以完成向HBase存储和检索数据,以及删除无效数据之类的操作。所有修改数据的操作都保证行级别的原子性,多个客户端或线程对同一行的读写操作都不会影响该行数据的原子性,要么读到最新的数据,要么等待系统允许写入行的修改。创建HTable实例是有代价的。每个实例都需要扫描HBase:meta表,以检查该表是否存在,是否可用。此外还有一些其他操作,这些检查和操作导致实例调用非常耗时。因此,推荐用户只创建一次HTable实例,而且是每个线程创建一个,如果用户需要使用多个HTable实例,应考虑使用HTablePool类,它可以复用多个HTable实例。
1.3.3.1 读Get
Get操作是指从客户端API中获取已存储数据的方法。HTable类中提供了get()方法,同时还有与之对应的Get类,Get操作返回一行或多行数据。Get()方法默认一次取回该行全部列的数据,我们可以限定只取回某个列族对应的列的数据,或者进一步限定只取回某些列的数据,之前也说过HBase的列的数据是多版本的,我们可以限定取回该列全部版本的数据或者限定只取回最新的一个或几个版本的数据。
当用户使用get()方法获取数据时,HBase返回的结果包含所有匹配的一单元格数据,这些数据将被封装在一个Result实例中返回给用户。用Result类提供的方法,可以从服务器端获取匹配指定行的特定返回值,这些值包括列族、列限定符和时间戳等。
1.3.3.2 写Put
Put操作要么向表增加新行(如果Key是新的)或更新行(如果Key已经存在)。可以一次向表中插人一行数据,也可以一次操作一个集合,同时向表中写入多行数据。如果要频繁修改某些行的数据,用户应该创建一个RowLock实例来防止其他用户对该行数据进行修改。
Put操作每次都会发起一次到服务器的RPC操作,如果有大量的数据要写入表中,就会有数千次RPC操作,这样效率很低。HBase客户端有一个缓冲区,负责将数据批量地仅通过一次RPC操作发送到服务端,这样可大大提高写入性能,默认客户端写缓冲区是关闭的,需要显式打开该选项。
当将一个Put集合提交到服务端的时候,可能会出现部分成功部分失败的情况,失败的数据会被保存到缓存区中进行重试。
HBase还提供了一个compare-and-set操作,这个操作先进行检查,条件满足后再执行,这个操作对于行是原子性的。
HBase没有Update操作,是通过Put操作完成对数据的修改的。
1.3.3.3 扫描Scan
Scan操作允许多行特定属性迭代,其使用与get()方法非常类似。工作方式类似于迭代器,可以指定startRow参数来定义扫描读取HBase表的起始行键,同时可选stopRow参数来限定读取到何处停止。
在创建Scan实例之后,用户可以增加更多的限定条件,没有参数将扫描整个表,包括所有列族以及所有列,可以用多种方法限定读取的数据。
扫描操作执行后将得到执行结果, 此结果被封装在一个ResultScanner实例中。下面是一个基于HTable表实例的示例:假设表有几个行键值为“row1”、“row2”、“row3",还有一些行键值为“abc1”、“abc2”和“abc3”。下面的代码展示了startRow和stopRow可以应用到一个Scan实例,以返回“row”开头的行。
Configution conf = HBaseConfiguration.create();
HTable htable = new HTable(conf, “table1”); //instantiate HTable
scan scan = new Scan();
scan.addColumn(Bytes.toBytes(“cf”),Bytes.toBytes(“attr”));
scan.setStartRow(Bytes.toBytes(“row”)); //start key is inclusive
scan.setStopRow(Bytes.toBytes(“row” + (char)0)); //stop key is exclusive
ResultScanner rs = htable.getScanner(scan);
try {
for (Result r = rs.next(); r != null; r = rs.next()) {
// process result…
} finally {
rs.close(); //always close the ResultScanner!
}
1.3.3.4 删除Delete
Delete用于从表中删除数据。HTable除了提供删除方法delete()外,还有一个与之对应的类Delete,用户可以通过多种方法限定要删除的列。
与关系型数据库的Delete操作不同,HBase的Delete操作可以指定删除某个列族或者某个列,或者指定某个时间戳,删除比这个时间早的数据。
HBase的Delete操作并不是真正地从磁盘删除数据。而是通过创建墓碑(tombstones)标志进行处理。这些墓碑标记的值和小于该时间版本的单元格在大合并(major compact)时被清除。
1.3.4 数据模型的特殊属性
讨论完HBase的基本概念,我们来学习一下它特有的功能。这些常常使人感到困惑,对使用过关系型数据库的读者来说尤为如此。HBase的这些特性使得完成某些功能更加方便,同时它也存在一些有待完善的地方,这也正是HBase在大力改进的地方。
1.3.4.1 版本
Rowkey、column(列族和列)、version组合在一起称为HBase中的一个单元格。有可能会有很多单元的行和列是相同的,要区分不同的单元可以使用版本。Rowkey和Column的值是用字节数组表示的,Version则是用一个长整型表示的。这个长整型值是使用java.util.Date.getTime()或者System.currentTimeMillis()产生的,这就意味着它的含义是“当前时间和1970-01-01UTC的时间差,单位毫秒”。
在HBase中,版本是按倒序排列的,因此当读取这个文件的时候,最先找到的是最近的版本。关于版本的一些常见问题主要有如下两个:
- Ø 如果有多个包含版本的写操作同时发起,HBase会保存全部还是会保持最新的一个?
- Ø 可以发起包含版本的写操作,但是它们的版本顺序和操作顺序相反吗?
下面介绍一下在HBase中版本是如何工作的。
1. 含版本的操作
这里详细讲解如何在HBase的各个主要操作中使用版本属性。
(1) Get/Scan
Get是在Scan的基础上实现的。Get同样可以用Scan来描述。在默认情况下,如果没有指定版本,一旦使用Get操作,会返回最近版本的Cell(该Cell可能是最新写入,但不能保证一定是)。默认的操作可以这样修改:
如果想要返回两个以上的版本,可以使用Get类的setMax Versions(),如果想要返回的版本不只是最近的,可以使用Get类的setTimeRange()。
要想查询的最新版本小于或等于给定的这个值,这就意味着给定的“最近”的值可以是某一个时间点。可以使用0到想要的时间来设置,还要把MaxVersionS设置为1。
默认Get例子如下,其中的Get操作会只获得最新的一个版本。
Get get = new Get(Bytes.toBytes("row1));
Result r = htable.get(get);
byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes(“attr”));
//returns current version of value
含有版本的Get例子如下,其中的Get操作会获得最近的3个版本。
Get get = new Get(Bytes.toBytes(“row1”));
get.setMaxVersions(3);
//will return last 3 versions of row
Result r = htable.get(get);
Byte[] b = r.getVable(Bytes.toBytes(“cf”), Bytes.toBytes(“attr”));
//returns current version of value
List<KeyValue> kv = r.getColumn(Bytes.toBytes(”cf”), Bytes.toBytes(“attr”));
//return all versions of this column
(2) Put操作
一个Put操作会为一个Cell创建一个版本,默认使用当前时间戳,当然也可以自己设置时间戳,这就意味着可以把时间设置在过去或者未来,或者随意使用一个长整型值。覆盖一个现有的值,就意味着新写入Cell的Rowkey、ColumnFamily、columnQulifier和version必须和原来的完全相同。
下面是不指明版本的例子,其中的Put操作不指明版本,所以HBase会将当前时间作为版本。
Put put = newPut(Bytes.toBytes(row));
put.add(Bytes.toBytes("cf"),Bytes.toBytes("attr1"),Bytes.toBytes(data));
htable.put(put);
下面是指明版本的例子,其中的Put操作指明了版本。
Put put = newPut(Bytes.toBytes(row));
long explicitTimelnMs = 555; //just an example
put.add(Bytes.toBytes("cf'"), Bytes.toBytes("attr1"), explicitTimeInMs,Bytes.toBytes(data));
htable.put(put);
(3) Delete
内部删除标记有三种不同类型:
- Delete:删除列的指定版本。
- DeleteColumn:删除列的所有版本。
- DeleteFamily:删除特定列族所有列。
当删除一行时,HBase将内部为每个列族创建墓碑(非每个单独列)标记。
删除操作的实现是创建一个墓碑标记。例如,要删除一个版本,HBase不会去改那些数据,数据不会立即从文件中删除。它使用删除标记来屏蔽掉这些值。如果被标记的是最新一个版本的数据,就意味着这一行中的所有数据都会被删除。
2. 现有的限制
关于版本还有一些未实现的功能,计划在以后的版本中实现。
(1) 删除标记后错误读取新数据
删除标记操作可能会标记其后Put的数据。注意,在写下一个墓碑标记后,只有下一个主合并(major ompact)操作发起之后,墓碑标记才会清除。假设删除所有小于等于时间T的数据,但之后又执行了一个Put操作,其时间戳小于等于T,那么就算这个Put发生在删除操作之后,该数据也会被打上墓碑标记。这个Put并不会失败,但你做Get操作时,则无法查询刚刚Put进去的数据。只有一个主合并(major compact)执行后,一切才会恢复正常。所以使用一系列时间戳一直增长的Put操作就不会发生该问题。
(2) 主合并改变查询的结果
一个Cell有三个版本数据t1、t2和t3,maxVersion设置为2,当请求获取全部版本的时候,只会返回两个:t2和t3。如果将t2和t3删除,就会返回t1。但是如果在删除之前,发生了major compaction操作,t1的值将会从磁盘上被彻底删除,结果是什么值都不会返回了。
1.3.4.2 排序
Get和Scan操作返回的是经过排序的数据。列在服务器端也是字典排序的,所以按照名称的字典序返回。总体来说,返回的数据首先按行字典序排序,其次是列族,然后是列修饰符(column qualifier),最后是时间戳反向排序,最新的在最前面。
1.3.4.3 列的元数据
对于HBase表中的列族,除了KeyValue实例以外,没有关于元数据的描述,KeyValue对象表示HBase的最小单位是Cell。HBase的表不仅在一行中支持很多列,而且支持行之间有不同的列,所以需要单独维护行和列之间的关系。获取列族的完整列名的唯一方法是处理所有行。
1.3.4.4 连接查询
HBase是否支持连接查询,即Join查询,是一个常见问题。简单来说是不支持,至少不像传统RDBMS那样支持。正如前面描述的,读数据模型只有Get和Scan两种操作。但这并不表示Join查询不能在应用程序中使用,只是必须用户自己实现。一般来讲,实现方法有两种:要么写入HBase的时候已经做好连接;要么查询并在应用层实现连接。哪个更好?依赖于准备做什么,所以没有一个准确的答案适合所有情况。
1.3.4.5 计数器
Increment Columnvalue(简称ICV)是HBase的级计数器,可以使用它完成一些诸如计算页面浏览量(PV)的操作。如果没有ICV,则需要首先读出HBase单元格中的值,然后加1再存入。ICV操作发生在RegionServer上,而不是在客户端,所以速度快,使用方式也没有那么烦琐。当其他客户端也在访问同一个单元格时,可以避免出现不一致的情况。可以把ICV等同于Java的AtomicLong.addAndGet()方法。
1.3.4.6 原子操作
类似Java的原子类,HTbleInterface接口也提供checkAndPut()和checkAndDelete()方法,它们可以在维持原子语义的同时提供更精细的控制。可以用checkAndPut()来实现上一节提到的incrementColumnValue()方法:
Configuration conf = HBaseConfiguration.create();
HTable htable = new HTable(conf,"table1"); //instantiate HTable
Get g = new Get(Bytes.toBytes(rowkey));
Resultr = htable.get(g);
long curVal = Bytes.tolong(r•getColumnLatest(Bytes.toBytes(family),
Bytes.toBytes(qualifier)).getValue());
long incVal = curVal+1;
Put P = new Put(Bytes.toBytes(rowkey));
P.add(Bytes.toBytes(family), Bytes.toBytes(qualifier), Bytes.toBytes(incVal));
htable.checkAndPut(Bytes.toBytes(rowkey),Bytes.toBytes(family),
Bytes.toBytes(qualifier), Bytes.toBytes(curVal),P);
上面的代码虽然有点长,但可以试试。使用checkAndDelete()的方式与此类似。
1.3.4.7 事务特性ACID
传统的SQL数据库的事务通常都是支持ACID的强事务机制。而HBase这种NoSQL数据库仅提供对行级别的原子性保证,也就是说同时对同一个Key下的数据进行的两个操作,在实际执行的时候是会串行的执行,保证了每一个KeyValue对不会被破坏。
之前版本的HBase提供行级的事务,不过每次事务只能执行一个写操作,假如连续地执行一系列Put、Delete操作,那么这些操作是单独一个个的事务,其整体并不是原子性执行的。而在0.94.*版本中,可以实现Put、Delete在同一个事务中一起原子性执行。
同一Region有多行原子性,因此对一个多Region表来说,还是无法保证每次修改都能封装为一个事务。HBase不是一个具备完整ACID特性的数据库,它只实现了某些属性。
HBase的ACID操作是复杂的,下面总结了ACID操作的一些主要原则,这些原则可以由点及面,逐步了解HBaseACID的特征。
HBase中考虑了事务(ACID)特性的数据操作包含以下这些:
- Ø 获取数据的API: Get、Scan
- Ø 修改数据的API: Put、Batch put、Delete
- Ø 多项操作在一起的API: IncrementColumnValue、CheckAndPut
1) 关于HBaseACID设计原则如下:对于同一行所有列的修改是原子性的,对于该行的Put操作要么整体成功要么整体失败。
2) 一个返回“成功”标志的操作肯定是完全成功的。
3) 一个返回“失败”标志的操作肯定是完全失败的。
4) 超时的操作可能成功也可能失败。但也不会是部分成功或失败。
5) 对于同一行跨多个列族的操作也遵循上面的原则。
6) 多行操作不能保证原子性,例如:对a、b、c三行进行操作,一些可能有返回,一些可能没有,在这种情况下,API会返回一个对这三行操作的结果列表,包括成功、失败或者超时。
7) CheckAndPut()操作就像许多编程语言的CompareAndset()操作一样是原子性的。
8) 所有修改操作是保证顺序的,例如:如果一个写操作将数据修改成”a=1,b=1,c=1",另一个修改成,“a=2,b=2,c=2",那么行的状态肯定是”a=1,b=1,c=1”或者”a=2,b=2,c=2",不可能出现”a=1,b=2,c=1”这种状态。
9) 请注意批量修改操作不能跨越多行。
10) 一致性和隔离性。通过API得到的行的数据是一个完整的行,数据由表中某个时刻的数据构成。
11) 持久性。所有可以读取到的数据保证都是已经被持久化到磁盘上的。也就是说不会读到没有写到磁盘上的数据。所有返回成功的操作的数据都是处于持久化到磁盘上的。返回失败的都没有持久化。
关于HBase的ACID语义详情可以参见:http://HBase.apache.org/acid-semantics.html。
1.3.4.8 行锁
HBase API中put()、delete()、check AndPut()等修改操作是独立执行的,这意味着在一个串行方式的执行中,对于每一行必须保证行级别的操作是原子性的。RegionServer提供了一个行锁特性,这个特性保证了只有一个客户端能获取一行数据相应的锁,同时对该行进行修改。
1.3.4.9 自动分区
HBase中一个表的数据会被划分成很多的Region,Region可以动态扩展并且HBase保证Region的负载均衡。Region实际上是行键排序后的按规则分割的连续的存储空间。如果Region太大,会被动态拆分,相反,多个Region会合并成一个较大的Region,以减少HDFS上存储文件的数量。这两个过程就是HBase的split和compaction。
刚刚创建的表只有一个Region,随着数据的写入,达到Region上限配置值时,Region会按照中间键自动地拆分成两个大致相等的Region,每个Region由一个RegionServer管理,一个RegionServer处理器管理着许多的Region。图 1.3.3展示了多个Region是如何分布在不同RegionServer上的,注意每个Region包含起始Rowkey的记录,不包含结束Rowkey的记录。
图 1.3.3 Region物理分布
每个RegionServer管理多少个Region合适?每个Region大小是多少合适?按照现在主流硬件的配置,每个RegionServer可以管理大约100~1000个Region,每个Region的大小可以是1~20GB。
Region的拆分和转移是由HBase自动完成的,用户感知不到,当一个RegionServer服务器发生故障时,Region可以快速地被转移到其他服务器上,Region的拆分过程也是瞬间完成的。当Region进行拆分时,首先要将该Region下线(offline),拆分完成后新的Region再上线(online),下线的Region暂时不可用,不过由于速度极快,通常不会对数据的读写造成影响。
1.3.5 CAP原理与最终一致性
CAP原理是数据库软件的理论基础,它指出对于一个数据库系统来说,不可能同时满足以下三点:
- Ø 一致性(Consistency):所有节点在同一时间具有相同的数据。
- Ø 可用性(Availability):保证每个请求不管成功或者失败都有响应。
- Ø 分区容忍性(Partition tolerance):系统中任意信息的丢失或失败不会影响系统的继续运作。
分布式数据库系统也只能满足三项中的两项。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的,因此只能在一致性和可用性之间进行权衡,大多数的分布式数据库系统选择了牺牲一致性提高可用性。图 1.3.3所示是不同数据库系统在这三方面的侧重点。
图 1.3.4 CAP模型及各种数据库分类
HBase的设计基于这样一些方面考虑,首先不要求严格的数据库事务,保证数据最终一致即可;其次数据库的写入可能在几秒之后读取出来用户也是能够忍受的,也就是说不能实时地读取刚刚写入的数据,另外就是复杂SQL的查询在产品设计阶段就避免了,更多的查询集中在针对主键的查询。
1.3.6 小结
HBase作为NoSQL数据库的一个代表和传统关系型数据库在概念上有相似之处,但也有非常大的差别,HBase更多的是为了扩展性和性能考虑,弱化了事务性,广大读者在学习时需要带着一个全新的思维方式来认识HBase,本节对HBase最重要的概念全部进行了概述,在后面的章节将分别针对各个方面进行详细描述。鼓励大家将HBase应用于实际的项目中,经过实践的积累会越发地感受到HBase在解决特定问题时所带来的优势。
相关文章:
分布式存储—— HBase数据模型 详解
目录 1.3 HBase数据模型 1.3.1 两类数据模型 1.3.2 数据模型的重要概念 1.3.3 数据模型的操作 1.3.4 数据模型的特殊属性 1.3.5 CAP原理与最终一致性 1.3.6 小结 本文章参考、总结于学校教材课本《HBase开发与应用》 1.3 HBase数据模型 在开始学习HBase之前非常…...
使用AI整理知识点--WPF动画核心知识
一、WPF动画基础 1、动画本质 通过随时间改变依赖属性值实现视觉效果(如位置、透明度、颜色等)。 依赖属性必须支持 DependencyProperty,且需是可动画的(如 Double, Color, Point 等)。 2、动画三要素 起始值 (Fr…...
计算光学成像与光学计算概论
计算光学成像所涉及研究的内容非常广泛,虽然计算光学成像的研究内容是发散的,但目的都是一致的:如何让相机记录到客观实物更丰富的信息,延伸并扩展人眼的视觉感知。总的来说,计算光学成像现阶段已经取得了很多令人振奋…...
100天精通Python(爬虫篇)——第115天:爬虫在线小工具_Curl转python爬虫代码工具(快速构建初始爬虫代码)
文章目录 一、curl是什么?二、爬虫在线小工具(牛逼puls)三、实战操作 一、curl是什么? 基本概念:curl 支持多种协议,如 HTTP、HTTPS、FTP、SFTP 等,可用于从服务器获取数据或向服务器发送数据&a…...
点云软件VeloView开发环境搭建与编译
官方编译说明 LidarView / LidarView-Superbuild GitLab 我的编译过程: 安装vs2019,windows sdk,qt5.14.2(没安装到5.15.7),git,cmake3.31,python3.7.9,ninja下载放到…...
PHP配置虚拟主机
虚拟主机: 不是真实存在的主机, 因为一台电脑理论上讲只能作为一个网站: 事实上,一个网站是一个文件夹. 在本地开发中,通过虚拟主机配置可以实现多域名独立访问不同项目目录(如 www.project1.test 和 www.project2.test),以 ”XAM…...
笔记四:C语言中的文件和文件操作
Faye:只要有正确的伴奏,什么都能变成好旋律。 ---------《寻找天堂》 目录 一、文件介绍 1.1程序文件 1.2 数据文件 1.3 文件名 二、文件的打开和关闭 2.1 文件指针 2.2.文件的打开和关闭 2.3 文件读取结束的判定 三、 文件的顺序读写 3.1 顺序读写…...
PyTorch中的线性变换:nn.Parameter VS nn.Linear
self.weight nn.Parameter(torch.randn(in_channels, out_channels)) 和 self.linear nn.Linear(in_channels, out_channels) 并不完全一致,尽管它们都可以用于实现线性变换(即全连接层),但它们的使用方式和内部实现有所不同。 …...
计算机网络(1) 网络通信基础,协议介绍,通信框架
网络结构模式 C/S-----客户端和服务器 B/S -----浏览器服务器 MAC地址 每一个网卡都拥有独一无二的48位串行号,也即MAC地址,也叫做物理地址、硬件地址或者是局域网地址 MAC地址表示为12个16进制数 如00-16-EA-AE-3C-40 (每一个数可以用四个…...
PHP之常量
在你有别的编程语言的基础下,你想学习PHP,可能要了解的一些关于常量的信息。 PHP中的常量不用指定数据类型,可以使用两次方法定义。 使用const //定义常量 const B 2; echo B . PHP_EOL;使用define define("A", 1); echo A . P…...
苦瓜书盘官网,免费pdf/mobi电子书下载网站
苦瓜书盘(kgbook)是一个专注于提供6英寸PDF和MOBI格式电子书的免费下载平台,专为电子阅读器用户设计。该平台为用户提供了丰富的电子书资源,涵盖文学、历史、科学、技术等多个领域,旨在打造一个全面的电子书资源库。用…...
通过 Docker openssl 容器生成生成Nginx证书文件
使用 alpine/openssl 镜像生成证书 1. 拉取容器 [rootlocalhost ~]# docker run --rm alpine/openssl version OpenSSL 3.3.3 11 Feb 2025 (Library: OpenSSL 3.3.3 11 Feb 2025)2. 运行 alpine/openssl 生成证书(Nginx) # 生成1个.key私钥文件&#…...
第四十一:Axios 模型的 get ,post请求
Axios 的 get 请求方式 9.双向数据绑定 v-model - 邓瑞编程 Axios 的 post 请求方式:...
从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(十) 收发消息
1.聊天框 首先我们完善前端的消息输入框 components下面新建MessageInput组件 import { useState,useRef } from "react" import {X,Image,Send} from "lucide-react"import { useChatStore } from "../store/useChatStore" import toast from…...
Spring Boot面试问答
1. Spring Boot 基础知识 问题 1:什么是Spring Boot?它与Spring框架有何不同? 回答: Spring Boot是基于Spring框架的一个开源框架,旨在简化新Spring应用的初始化和开发过程。与传统的Spring框架相比,Spring Boot提供了以下优势: 自动配置:根据项目依赖自动配置Spring…...
win11编译llama_cpp_python cuda128 RTX30/40/50版本
Geforce 50xx系显卡最低支持cuda128,llama_cpp_python官方源只有cpu版本,没有cuda版本,所以自己基于0.3.5版本源码编译一个RTX 30xx/40xx/50xx版本。 1. 前置条件 1. 访问https://developer.download.nvidia.cn/compute/cuda/12.8.0/local_…...
2025-03-07 学习记录--C/C++-C语言 截取字符串的三种方法
C语言 截取字符串 ⭐️ 方法 1:使用 strncpy 函数 🍭 strncpy 是C标准库中的一个函数,用于从源字符串中复制指定长度的字符到目标字符串中。【详情请查看我的另一篇文章】 示例代码:🌰 #include <stdio.h> #in…...
doris: Oracle
Apache Doris JDBC Catalog 支持通过标准 JDBC 接口连接 Oracle 数据库。本文档介绍如何配置 Oracle 数据库连接。 使用须知 要连接到 Oracle 数据库,您需要 Oracle 19c, 18c, 12c, 11g 或 10g。 Oracle 数据库的 JDBC 驱动程序,您可以从 Maven 仓库…...
【神经网络】python实现神经网络(一)——数据集获取
一.概述 在文章【机器学习】一个例子带你了解神经网络是什么中,我们大致了解神经网络的正向信息传导、反向传导以及学习过程的大致流程,现在我们正式开始进行代码的实现,首先我们来实现第一步的运算过程模拟讲解:正向传导。本次代…...
自学嵌入式第27天------TCP和UDP,URL爬虫
1. TCP和UDP区别 **TCP(传输控制协议)和UDP(用户数据报协议)**是两种主要的传输层协议,它们在数据传输方式上有显著区别: 连接性: TCP是面向连接的协议,通信前需通过三次握手建立连…...
【虚拟化】Docker Desktop 架构简介
在阅读前您需要了解 docker 架构:Docker architecture WSL 技术:什么是 WSL 2 1.Hyper-V backend 我们知道,Docker Desktop 最开始的架构的后端是采用的 Hyper-V。 Docker daemon (dockerd) 运行在一个 Linux distro (LinuxKit build) 中&…...
C#程序加密与解密Demo程序示例
目录 一、加密程序功能介绍 1、加密用途 2、功能 3、程序说明 4、加密过程 5、授权的注册文件保存方式 二、加密程序使用步骤 1、步骤一 编辑2、步骤二 3、步骤三 4、步骤四 三、核心代码说明 1、获取电脑CPU 信息 2、获取硬盘卷标号 3、机器码生成 3、 生成…...
200W数据需要去重,如何优化?
优化去重逻辑的时间取决于多个因素,包括数据量、数据结构、硬件性能(CPU、内存)、去重算法的实现方式等。以下是对优化去重逻辑的详细分析和预期优化效果: 1. 去重逻辑的性能瓶颈 时间复杂度:使用HashSet去重的时间复…...
理解 UDP 协议与实战:Android 使用 UDP 发送和接收消息
一、UDP 协议概述 UDP(User Datagram Protocol,用户数据报协议)是一个无连接的网络通信协议。与 TCP 不同,UDP 不建立连接,不保证数据的顺序和完整性。它的特点是简单、高效,适用于实时性要求较高、对数据…...
7V 至 30V 的超宽 VIN 输入范围,转换效率高达 96%的WD5030
WD5030 具备 7V 至 30V 的超宽 VIN 输入范围,这一特性使其能够适应多种不同电压等级的供电环境,无论是在工业设备中常见的较高电压输入,还是在一些便携式设备经过初步升压后的电压,WD5030 都能轻松应对,极大地拓展了应…...
基于 LeNet 网络的 MNIST 数据集图像分类
1.LeNet的原始实验数据集MNIST 名称:MNIST手写数字数据集 数据类型:灰度图 (一通道) 图像大小:28*28 类别数:10类(数字0-9) 1.通过torchvision.datasets.MNIST下载并保存到本地…...
智能体开发:推理-行动(ReAct)思维链提示
人类在处理一个需要多个步骤才能完成任务时,显著特点是能够将言语推理(内心独白)和实际行动融合在一起,在面对陌生或不确定的情况时通过这种方法学习新知识,做出决策,并执行,从而应对复杂的任务…...
Android Native 之 文件系统挂载
一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知,init进程为android系统的第一个进程,也是native世界的开端,要想让整个android世界能够稳定的运行,文件系统的创建和初始化是必不可少的ÿ…...
DirectX12(D3D12)基础教程四 入门指南
本章主要讲了些D3D12概念和理论,对第一、二章相关概念的补充和纠正,要的理解D3D12概念和理论基础,结合代码加深理解。 命令队列和命令列表 为了实现渲染工作的重用和多线程缩放, 在 D3D12 中,做了三个重要方面不同于 …...
android13打基础: timepicker控件
public class Ch4_TimePickerActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener {private TextView tv_time; // 声明一个文本视图对象private TimePicker tp_time; // 声明一个时间选择器对象Overrideprotected void onCreate(Nullable Bund…...
国产化板卡设计原理图:2330-基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡
基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于 FPGAJFM7K325T 芯片,pin_to_pin兼容FPGAXC7K410T-2FFG900 ,支持PCIeX8、64bit DDR3容量2GByte,HPC的FMC连接器,板卡支持PXIE标准协议,其中XJ3…...
Oracle SQL优化①——查看SQL执行计划的几种方法
前言 在日常的运维工作中,SQL优化是DBA的进阶技能,SQL优化的前提是要看SQL的执行计划是否正确,下面分享几种查看执行计划的方法,每一种方法都各有各的好处,可以根据特定场景选择某种方法。 一.使用AUTOTRACE查看执行…...
MySQL主从架构配合ShardingJdbc实现读写分离
文章目录 目录架构搭建读写分离pom.xmlfdy-live-user-provider 模块application.ymlfdy-db-sharding.yamlShardingJdbcDatasourceAutoInitConnectionConfig.java 目录 架构搭建 基于Docker去创建MySQL的主从架构 读写分离 pom.xml <dependency><groupId>mysql…...
MyBatis @Param 注解详解:多参数传递与正确使用方式
Param 注解主要用于 MyBatis 进行参数传递时给 SQL 语句中的参数 起别名,通常用于 多参数 方法,使参数在 XML Mapper 文件或注解 SQL 语句中更清晰易用。 1. 基本用法 在 Mapper 接口中使用 Param 来为参数命名,避免 MyBatis 解析时出现参数…...
【JAVA架构师成长之路】【Redis】第14集:Redis缓存穿透原理、规避、解决方案
30分钟自学教程:Redis缓存穿透原理与解决方案 目标 理解缓存穿透的成因及危害。掌握布隆过滤器、空值缓存等核心防御技术。能够通过代码实现请求拦截与缓存保护。学会限流降级、异步加载等应急方案。 教程内容 0~2分钟:缓存穿透的定义与核心原因 定义…...
mybatis报错org/apache/commons/lang3/tuple/Pair] with root cause
mybatis一对多查询配置resultMap映射报错org/apache/commons/lang3/tuple/Pair] with root cause 原因是mybatis依赖common-lang3这个包, 只需要添加common-lang3的依赖坐标即可: <dependency><groupId>org.apache.commons</groupId><artifactId>comm…...
supervisord管理Gunicorn进程,使用Nginx作为反向代理运行flask web项目
1. 安装 Gunicorn 在项目虚拟环境中安装 Gunicorn:2. 基本用法 配置文件 创建一个 Gunicorn 配置文件(如 gunicorn_config.py),方便管理复杂配置。 示例 gunicorn_config.py: bind "0.0.0.0:8000" #…...
STM32之软件SPI
SPI传输更快,最大可达80MHz,而I2C最大只有3.4MHz。输入输出是分开的,可以同时输出输入。是同步全双工。仅支持一主多从。SS是从机选择线。每个从机一根。SPI无应答机制的设计。 注意:所有设备需要共地,时钟线主机输出&…...
fastjson漏洞
fastjson漏洞 fastjson工作原理攻击原理补充 例子 fastjson工作原理 fastjson的作用是将JAVA对象转换成对应的json表示形式,也可以反过来将json转化为对应的Java对象。fastjson使用AutoType功能进行反序列化,AutoType使用type标记字符的原始类型&#x…...
【YOLOv12改进trick】医学图像分割网络CMUNeXt引入YOLOv12中,增强全局上下文信息实现涨点,含创新点Python代码,方便发论文
🍋改进模块🍋:医学全卷积U型网络(CMUNeXtBlock) 🍋解决问题🍋:CMUNeXtBlock模块解决了在医学图像分割任务中,传统的卷积操作由于其局部性限制,难以有效提取全局上下文信息的问题。 🍋改进优势🍋:利用大卷积核和倒置瓶颈结构,提取全局上下文信息,同时保持卷…...
机器学习的半监督学习,弱监督学习,自监督学习
半监督学习、弱监督学习和自监督学习是机器学习中利用不同形式监督信号的三种方法,它们的核心区别在于标注数据的数量、质量以及监督信号的来源。以下是它们的详细对比: 1. 半监督学习(Semi-Supervised Learning) 核心特点&#x…...
K8S学习之基础十七:k8s的蓝绿部署
蓝绿部署概述 蓝绿部署中,一共有两套系统,一套是正在提供服务的系统,一套是准备发布的系统。两套系统都是功能完善、正在运行的系统,只是版本和对外服务情况不同。 开发新版本,要用新版本替换线上的旧版本&…...
【计算机网络】计算机网络的性能指标——时延、时延带宽积、往返时延、信道利用率
计算机网络的性能指标 导读 大家好,很高兴又和大家见面啦!!! 在上一篇内容中我们介绍了计算机网络的三个性能指标——速率、带宽和吞吐量。用大白话来说就是:网速、最高网速和实时网速。 相信大家看到这三个词应该就…...
单片机如何用C语言重写vfprintf
在单片机中,标准库函数 vfprintf 可能不可用或占用过多资源(如内存或代码空间),因此我们可以通过自定义实现一个轻量级的 vfprintf 函数,专门用于单片机的串口输出。以下是一个用 C 语言重写 vfprintf 的示例ÿ…...
1.4 单元测试与热部署
本次实战实现Spring Boot的单元测试与热部署功能。单元测试方面,通过JUnit和Mockito等工具,结合SpringBootTest注解,可以模拟真实环境对应用组件进行独立测试,验证逻辑正确性,提升代码质量。具体演示了HelloWorld01和H…...
使用免费IP数据库离线查询IP归属地
一、准备工作 1.下载免费IP数据库 首先,访问 MaxMind官网(https://www.maxmind.com/en/home)如果你还没有MaxMind账号,可以通过此链接地址(https://www.maxmind.com/en/geolite2/signup)进行账号注册&…...
go语言数据类型
一.Go 语言有四大显著特点:高效性:编译与运行效率高。并发性:原生支持并发编程。简洁性:编程风格简洁直接,代码可读性与可维护性高。跨平台性:程序能在 Windows、Linux、Mac OS 等多系统运行。二.go的包:pa…...
操作系统 1.3-开机页面的产生
问题引入 从打开电源开始.. 这神秘的黑色背后发生着什么?... 设计思路 伟大的数学家图灵,设计了这样的一个模型: 首先我们计算机有一个控制器,他可以处理一系列提供给他的动作。我们将控制器动作,控制器状态,数据…...
单例模式:确保一个类只有一个实例
目录 引言 1. 单例模式的核心思想 2. 单例模式的实现方式 2.1 饿汉式单例 2.2 懒汉式单例 2.3 线程安全的懒汉式单例 2.4 双重检查锁定(Double-Checked Locking) 2.5 静态内部类实现单例 2.6 枚举实现单例 3. 单例模式的使用场景 4. 单例模式…...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_modules
定义在 objs\ngx_modules.c #include <ngx_config.h> #include <ngx_core.h>extern ngx_module_t ngx_core_module; extern ngx_module_t ngx_errlog_module; extern ngx_module_t ngx_conf_module; extern ngx_module_t ngx_openssl_module; extern ngx_modul…...