- Chapter 2.2.3 & 2.2.5
- 2.2.3 Under the hood: the Hbase write path —— Hbase写路径
- 2.2.5 Under the hood: the Hbase read path —— Hbase读路径
- Chapter 4.2 De-normalization —— 去规范化
- Chapter 4.5 I/O consideration —— I/O考虑
- Optimized for writes —— hashing & salting
- Optimized for reads —— put the last few data together
- Chapter 4.6 From relational to non-relational
- Some basic concepts
- Some things don’t map
Hbase上RegionServer的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分作为BlockCache,主要用于读数据。以下是对Hbase读写 *** 作的详细描述。
2.2.3 Under the hood: the Hbase write path —— Hbase写路径Hbase写入 *** 作包括两个部分:把写入 *** 作分别记录到预写日志(WAL)和MemStore,以保证数据持久化。每只有当WAL和MemStore的变化信息都写入并确认后,才认为写 *** 作完成。写路径如图所示:
MemStore是内存里的写入缓冲区,Hbase中数据在永久写入磁盘前在MemStore中累积。当MemStore填满/达到其阈值后,其中的数据会刷写(flush)到磁盘,生成一个HFile。HFile对应列族,一个列族可以有多个HFile,但一个HFile只能存储一个列族的数据。在集群的每个节点上,每个列族有一个Memstore。
如果MemStore还没有将数据刷写到磁盘,服务器就崩溃了,内存中的数据就会丢失。因此,Hbase采用WAL应对硬件故障:在写 *** 作完成之前先写入WAL。Hbase集群中每台服务器维护一个WAL来记录发生的变化。WAL新记录成功写入后,写 *** 作才被认为成功完成。
那么如果Hbase服务器宕机,没有从MemStore里刷写到HFile的数据可以通过回放WAL来恢复(Handled under the hood by Hbase as a part of the recovery process)。
可见,写入WAL是写过程中非常重要的一部分。如果不写入WAL,服务器故障时Hbase可能无法恢复数据,没有刷写到磁盘的所有写入数据都会丢失。MemStore和WAL确保每次写入Hbase在尽可能快的同时保证持久性。
2.2.5 Under the hood: the Hbase read path —— Hbase读路径Hbase能够保证通常情况下的快速读取(allowing it to serve millisecond reads in most cases)。
Hbase在读 *** 作上使用LRU(Least Recent Used)缓存,也叫BlockCache。BlockCache目的是把经常访问的、来自HFile的数据缓存在内存中,避免读取磁盘。每个列族都有自己的BlockCache。
BlockCache中的Block是Hbase一次从磁盘读取的数据单元。HFile物理上是一系列的block加上一个这些block的索引。这意味着,读取一个block只需在索引中查找该block的位置,再从磁盘获取。
Block 是建立索引的最小数据单位,也是从硬盘读取的最小数据单位。Block大小按照列族设定,默认值是64 KB。根据使用场景你可以调整block的大小。如果主要用于随机查询,你可能需要细粒度的Block 索引,小一点儿的Block 更好一些。但Block变小会导致索引变大,进而消耗更多内存。如果用于顺序扫描,一次读取多个Block,大一点儿的Block 更好一些。Block 变大意味着索引项变少,索引变小,因此节省内存。
从Hbase 中读出一行时,首先会检查MemStore 等待修改的队列,然后检查BlockCache看包含该行的Block 是否最近被访问过,最后访问硬盘上的对应HFile。读路径如图所示:
Normalization对应的数据属于干净非冗余型。这有两个好处:当发生更新或删除时,不必担心更新给定数据所有副本的复杂性;通过保存单一副本而不是多个副本来,减少了占用的存储空间。在查询时需要使用SQL语句中的JOIN子句重新联结数据。
而De-normalization则允许数据冗余或者同样的数据存储于多处。这使得查询数据更容易、更快。(不需要昂贵、耗时的JOIN *** 作)
从性能的角度来看,规范化对写 *** 作进行优化,而去规范化对读 *** 作进行优化。
Normalization optimizes the table for writes; you pay the cost of combining data at read time. De-normalization optimizes for reads, but you pay the cost of writing multiple copies at write time.
以Twitbase为例,当一个用户登录帐户并希望看到他们关注的人的推帖(twits)时,如果是采用规范化的关系数据库,app将获取被关注的用户和他们的推帖,并返回该信息。随着系统中用户数量的增加,这个过程可能非常耗时。此外,如果一个用户有很多followers,那么每次有follower登录时都会访问他们的推帖。管理这个用户推帖的region则会不断地回答请求(read hot pot)。解决这个问题的方法是为每个用户维护一个推帖流(twit stream),当他们关注的用户发推帖时,将该推帖添加至推帖流里。由此优化读的性能。
Chapter 4.5 I/O consideration —— I/O考虑 Optimized for writes —— hashing & salting 当往Hbase写入大量数据时,我们希望在RegionServer分散负载来对写 *** 作优化。以时间序列数据为例:如果直接以时间戳作为行键,那么写入时在单个region上会遇到热点问题。如何把数据分布在多个region上?
-
Hashing
如果写入需要扫描整个表或者每次都需要知道精确的键,使用原始数据的散列值作为行键。可以使用MD5、SHA-1等散列函数
-
Salting
如果在读取时知道时间范围,不想做全表扫描,可以在时间戳前面加上一个随机数前缀。
例:先计算时间戳的散列码,然后对RegionServer的数量取模生成随机salt数:
int salt = new Integer(new Long(timestamp).hashCode()).shortValue() %
取得salt数后,加到时间戳前面生成行键:
byte[] rowkey = Bytes.add(Bytes.toBytes(salt) + Bytes.toBytes("|") + Bytes.toBytes(timestamp)); Now rowkeys are something like: 0|timestamp1 0|timestamp5 0|timestamp6 1|timestamp2 1|timestamp9 2|timestamp4 2|timestamp8
这些行将基于前缀,分布在不同的region里。
Optimized for reads —— put the last few data together 以之前的Twitbase为例,为读优化行键时,将twit流中最新的twits存储在一起,以便于它们可以被快速读取。使用倒序时间戳+用户ID构成行键。那么基于用户ID扫描邻近的n行就可以找到用户需要的n条最新的twits。
Chapter 4.6 From relational to non-relationalSome basic conceptsRelational databases and Hbase are different systems and have different design properties that affect application design. A naïve migration from relational to Hbase is tricky.
-
实体 Entity
table ——> table 关系数据库和Hbase中,实体都是由表来表示的,表中 的每行代表实体的一个实例。
-
属性 Attribute
a. identifying attribute ——> rowkey b. non-identifying attribute ——> column
-
联系 Relationship
关系数据库:foreign key(one to one) or junction table(one to many) Hbase:没有对于联系的直接映射
-
列族
One way of thinking about column families is that they model yet another kind of relationship that we didn’t talk about previously: the one-to-one relationship, where you have two tables with the same primary key, and each table has either 0 or 1 row with each primary key value.
Using multiple column families in Hbase is an advanced feature that you should only jump into if you’re sure you understand the trade-offs.
Column是Hbase最基本的单位,若干列形成column family。关系数据库的每一列是属性,没有列族的概念。
-
索引
In relational databases, the ability to easily declare indexes that are automatically maintained by the database engine is one of the most magical and helpful capabilities the software provides, and it’s nowhere to be found in Hbase. For now, the answer to this question is: tough luck.
Hbase没有索引。(怎么理解这里的tough luck?)
-
时间版本
If in your relational schema, there are places where you explicitly store timestamps, these can in many cases be subsumed into the timestamp that’s stored in Hbase cells places where you explicitly store timestamps, these can in many cases be subsumed into the timestamp that’s stored in Hbase cells
Hbase涉及到时间维度,而关系型数据库的设计并没有把时间看成一个特殊的维度,而是显式地存储时间戳。通常涉及到记录时间戳的采用Hbase。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)