Java学习-知识汇总

                              Java学习-知识汇总

学习内容:https://m.toutiaocdn.com/group/6733459544518689292/?app=news_article_lite&timestamp=1567984792&req_id=2019090907195101002302815705D103FD&group_id=6733459544518689292

ç¾å¢æ¶æå¸è°å¼åï¼å¨å°å¬å¸çJavaç¨åºåè½æ¿30Ké«èªï¼

一、设计模式:

     1、代理模式

      静态代理:使用接口+实现类,在运行之前,就确定好代理类、被代理类之间的关系。

      动态代理:在运行时动态的创建一个代理类,实现一个或多个接口,将方法的调用转发到指定的类。利用Java的反射技术实现动态代理,主要是靠反射,并依赖于JDK中的java.lang.reflect.Proxy类、java.lang.ClassLoader类以及java.lang.reflect.InvocationHandler接口。

        地址:https://www.cnblogs.com/Scott007/p/3446703.html

2、工厂模式

         1)简单工厂模式:类似静态代理模式。创建时根据参数判定创建哪个类

2)普通工厂模式:类似静态代理模式。在方法内自己来实现。

3)抽象工厂模式:工厂模式看着不太好理解,不知道这样的优点是啥。

地址:https://www.cnblogs.com/xingele0917/p/3928207.html

3、单例模式:

     1)懒汉式:对方法进行sychnroized,损耗性能。

     2)饿汉式:直接static 定义的时候就new 出来,跟随系统启动加载,损耗内存。

     3)双重检查:注意一定要对static的定义增加volitedate关键字,保证线程安全可见。

     4)登记式/静态内部类:与第3中方式类似,但是使用的是静态域,但是static 是一个内部的类,保证需要时才加载。

地址:https://www.runoob.com/design-pattern/singleton-pattern.html

4、委派模式:

      1)在spring中的体现:Spring MVC框架中的DispatcherServlet其实就用到了委派模式。委派模式的作用: 基本作用就是负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果

å§æ´¾

5、策略模式:

策略模式:意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

ç­ç¥æ¨¡å¼ç UML å¾

https://www.runoob.com/design-pattern/strategy-pattern.html

6、原型模式:

克隆,原型模式是一个创建型的模式。原型二字表明了改模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。

型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件: 
  (1)实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。 
  (2)重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此Prototype类需要将clone方法的作用域修改为public类型。

7、代理模式:意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。主要解决:一些方法通用,却在每一个子类都重新写了这一方法。何时使用:有一些通用的方法。如何解决:将这些通用算法抽象出来。

 

一、司机轨迹的存储。

      背景:订单每1秒一个轨迹,10秒上传一次,有噪点,有距离较短的点(经纬度、时间、速度、方向、数量)。每天10W订单

      服务端:利用redis的缓冲队列,依次处理数据。检测噪点,速度过快,卫星数量过少,抛弃点。

                      聚合点按照 差值聚合,减少存储。缓存按照最后一个点存储。

      存储:按照订单ID分了1024个表,每个表按照订单ID+上传序号,加Json存储。即每个订单每10s存一条记录。

      容量预估:1条记录大概1KB*3600W*365=

      HBASE+RowKey设计:订单ID+时间戳  解决存储问题。

      里程的计算:每次分段计算+最后综合差值比较,有异常特殊处理。

    待和铁成讨论:

    1、历史轨迹按照订单ID存储的

    2、迁移过来的数据,按照rowkey切分,理论上就不会有超时了吧。为什么还要冷热数据切分。

    3、rowkey切分的原理。根据订单ID是如何查询的?

 

二、RocketMQ和kafka的底层架构是什么样子的 ,为什么可以承载这么大的消息量。

 

 

三、Hbase底层的原理,为什么可以存储PB级数据,Hbase的搜索设计。

首先,需要明确的是,Hbase写入速度比读取速度要快,根本原因LSM存储引擎

从存储引擎的角度分析

  • Hbase底层的存储引擎为LSM-Tree(Log-Structured Merge-Tree)。
  • LSM核心思想的核心就是放弃部分读能力,换取写入的最大化能力。LSM Tree ,这个概念就是结构化合并树的意思,它的核心思路其实非常简单,就是假定内存足够大,因此不需要每次有数据更新就必须将数据写入到磁盘中,而可以先将最新的数据驻留在内存中,等到积累到最后多之后,再使用归并排序的方式将内存内的数据合并追加到磁盘队尾(因为所有待排序的树都是有序的,可以通过合并排序的方式快速合并到一起)。
  • LSM树的设计思想非常朴素:将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,不过读取的时候稍微麻烦,需要合并磁盘中历史数据和内存中最近修改操作,所以写入性能大大提升,读取时可能需要先看是否命中内存,否则需要访问较多的磁盘文件。极端的说,基于LSM树实现的HBase的写性能比MySQL高了一个数量级,读性能低了一个数量级。
  • LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。

参考博客:https://blog.csdn.net/liaoxiangui/article/details/80597056

Hbase为什么读取速度快

  • HBase能提供实时计算服务主要原因是由其架构和底层的数据结构决定的,即由LSM-Tree(Log-Structured Merge-Tree) + HTable(region分区) + Cache决定——客户端可以直接定位到要查数据所在的HRegion server服务器,然后直接在服务器的一个region上查找要匹配的数据,并且这些数据部分是经过cache缓存的。
  • 前面说过HBase会将数据保存到内存中,在内存中的数据是有序的,如果内存空间满了,会刷写到HFile中,而在HFile中保存的内容也是有序的。当数据写入HFile后,内存中的数据会被丢弃。
  • HFile文件为磁盘顺序读取做了优化,按页存储。下图展示了在内存中多个块存储并归并到磁盘的过程,合并写入会产生新的结果块,最终多个块被合并为更大块。

     

    hbase读取速度快

  • 多次刷写后会产生很多小文件,后台线程会合并小文件组成大文件,这样磁盘查找会限制在少数几个数据存储文件中。HBase的写入速度快是因为它其实并不是真的立即写入文件中,而是先写入内存,随后异步刷入HFile。所以在客户端看来,写入速度很快。另外,写入时候将随机写入转换成顺序写,数据写入速度也很稳定。
  • 而读取速度快是因为它使用了LSM树型结构,而不是B或B+树。磁盘的顺序读取速度很快,但是相比而言,寻找磁道的速度就要慢很多。HBase的存储结构导致它需要磁盘寻道时间在可预测范围内,并且读取与所要查询的rowkey连续的任意数量的记录都不会引发额外的寻道开销。比如有5个存储文件,那么最多需要5次磁盘寻道就可以。而关系型数据库,即使有索引,也无法确定磁盘寻道次数。而且,HBase读取首先会在缓存(BlockCache)中查找,它采用了LRU(最近最少使用算法),如果缓存中没找到,会从内存中的MemStore中查找,只有这两个地方都找不到时,才会加载HFile中的内容,而上文也提到了读取HFile速度也会很快,因为节省了寻道开销。

为什么不用B+树

B+树虽然适合在磁盘中存储,并且从原理上来看它的速度应该会很快,但是它并非是顺序读写磁盘,例如它的节点进行分裂操作时在内存中会拆成两个新的页表,存储到磁盘上很可能就是不连续的;或者其他更新插入删除等操作,需要循环利用磁盘快,也会造成不连续问题。这也是HBase不使用B+树的根本原因,不进行优化的话随机I/O太多,范围查询和大量随机写时尤其明显。


————————————————
版权声明:本文为CSDN博主「淡定一生2333」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zc19921215/article/details/89283260

举例:
A:如果快速查询(从磁盘读数据),hbase是根据rowkey查询的,只要能快速的定位rowkey, 就能实现快速的查询,主要是以下因素:
1、hbase是可划分成多个region,你可以简单的理解为关系型数据库的多个分区。
2、键是排好序了的
3、按列存储的

  • 首先,能快速找到行所在的region(分区),假设表有10亿条记录,占空间1TB, 分列成了500个region, 1个region占2个G. 最多读取2G的记录,就能找到对应记录;
  • 其次,是按列存储的,其实是列族,假设分为3个列族,每个列族就是666M, 如果要查询的东西在其中1个列族上,1个列族包含1个或者多个HStoreFile,假设一个HStoreFile是128M, 该列族包含5个HStoreFile在磁盘上. 剩下的在内存中。
  • 再次,是排好序了的,你要的记录有可能在最前面,也有可能在最后面,假设在中间,我们只需遍历2.5个HStoreFile共300M
  • 最后,每个HStoreFile(HFile的封装),是以键值对(key-value)方式存储,只要遍历一个个数据块中的key的位置,并判断符合条件可以了。 一般key是有限的长度,假设跟value是1:19(忽略HFile上其它块),最终只需要15M就可获取的对应的记录,按照磁盘的访问100M/S,只需0.15秒。 加上块缓存机制(LRU原则),会取得更高的效率。

B:实时查询

  • 实时查询,可以认为是从内存中查询,一般响应时间在1秒内。HBase的机制是数据先写入到内存中,当数据量达到一定的量(如128M),再写入磁盘中, 在内存中,是不进行数据的更新或合并操作的,只增加数据,这使得用户的写操作只要进入内存中就可以立即返回,保证了HBase I/O的高性能。
  • 实时查询,即反应根据当前时间的数据,可以认为这些数据始终是在内存的,保证了数据的实时响应。

image

1、Hbase RowKey 是字节数组存储,最大长度64Kb(10~100个byte),rowkey数据 按照字节的字典序进行排序。设计Key时要了解这个特性,将经常读取的行放在一起,需要注意的是,每个行的操作都是原子操作,读取和写入。

2、Table 在行方向上分为多个Region,Region分布在多个Region Server上,每个Region 由多个Store组成,每个Store 由一个memStrore和多个 StoreFile组成。每个Store保存一个列族(Columns Family)

3、Hbase的每张表,都按照rowkey  分为多个Region,默认一个Region不能超过256M,超过以后由Region Server进行分割,而Region的位置有HBase Mater来处理。

4、Client访问HBase 不需要经过Hmaster,Hmaster 只负责Table和Region在Region Server的分配。寻址访问ZK+RegionServer,读写数据访问RegionServer。Table的元数据信息仅放在Zk上。 Region Server在存取一个子表时,会创建一个Region对下,对每个列族创建一个store对象。每个store 有 1个memstor和0或多个storefile。 storeFile存储在HDFS的HFile对象上。因此一个Region有多少个列族,就有多少个Stroe。一个Region Server上有多个Region和一个Region_log。

5、HRegion,Table在行方向上分为多个Region,Region是HBaes分布式存储和负载均衡的最小的拆分单元,不同的Region在不同的Region Server上,但是同一个Region不会切分到不同的Region Server上。每个表最初都只有一个Region,随着数据量的上涨,当某个列族达到了256M(默认值)的时候,就回拆分成两个Region。

6、HRegion的定位,HRegion 被分配给哪个Region Server是由HMaster来决定的,所以如何定位RowKey,在哪个Region Server下,Hbase在使用三层架构来定位。

1)通过zk中hbase/rs得到root表的位置,Root表只有一个Region。

2)通过-Root表查找.Media表第一个表中相应的HRegion的位置,其实Root表是Media表的第一个Region。Media表的每一个Region都是Root表中的一条记录。

3)通过Media表查找用户表HRegion个的位置,用户表的每个Region在Media表中都是一条记录。

Root表的Region永远不会拆分,保证了最多需要3次跳转,就可以定位到任意的Region,Client会将查询到的Region位置信息缓存起来,缓存不会主动失效。因此如果Client端缓存失效了,需要6次网络来回,才可以定位到Region的位置,3次用来确认缓存失效,3次用来获取Region位置。

7、Store:每个HRegion由一个或多Store组成,至少拥有1个。HBase会把一起访问的数据存储到一个Store里面,即每个列簇建立一个Store,有几个列簇就建立几个Store。一个Store由一个memStore和多个StoreFile组成。HBase以Store的大小来确定 是否切分HRegion(即按照列簇的大小)。

8、memStore:memStore是放在内存中的,保存修改的KeyValues,当memStore的数据超过了一定的阈值默认是64M以后,会将数据Flush到文件,即生成一个快照HFile。

9、StoreFile:memStore中每次Flush就是一个StoreFile。StoreFile底层以HFile的形式进行存储。

10、HFile: Hbase中KeyValue的存储格式,是Hadoop的二进制文件存储格式。首先HFile是不定长的,长度固定的只有其中两块,Tralier和FileIO。Trailer中有指针指向其他数据块的起点,FileIO中记录了文件的一些media信息。DataBlock是HBase IO的基本单元,为了提高效率,HRegionServer有基于LRU的BlockCache机制,每个data块的大小在创建table时可以 指定(默认是64kb)。大号的dat block利于scan查询,小号的data bolck利于随机查询。每个data块除了开头的 magic以外,就是多个keyvalue拼接而成。Magic的内容是一些随机数字,防止损坏。结构如下:

HFile的结构如下V1.0版本:

image

1、Data Block段用来保存数据,所以可以被压缩。Meta端用来存储用户自定义的KV段,可以被压缩。 Data Block Index段(可选的)用来保存Data Blcok的索引。HFile的DataBlock和MetaBlock 通常采用压缩的方式来进行存储。压缩后可以大大减少网络和磁盘IO,同时需要CPU进行压缩和解压缩。

2、FileIo用来存储HFile的元信息,不能被压缩。用户也可以在这一部分添加自己的元信息。

3、Trailer是定长的,用来保存每一个段的偏移量。读取一个HFile时,首先读取的是Trailer,Trailer中保存了每一个段的位置(段中的magic num用来做check),然后DataBlock Index会被读取到内存中。这样当检索每个Key的时候,就不用扫描 整个HFile,只需要通过内存中的DataBlock Index就可以找到文件的位置,通过一次磁盘IO就将这个Block读取到内存中 ,然后根据Key找到相应的数据。

4、Data Block Index 采用LRU淘汰机制,(备注: DataBlock Index的缺陷。 a) 占用过多内存 b) 启动加载时间缓慢)

HFileV1.0的问题  :

1、Data Block Index存储了每个Data Block的的索引信息{offset,size,firstkey},只有这个一级索引,当HFile较大时,索引信息过多,导致一个Region Server在启动时可能要加载GB级的Data Block Index数据,在一个大数据量的集群中这是无法忍受的。另外Region Server还需要加载所有的BloomFile数据,一个HFile中的BloomFile可以达到百M级别。

2、Data Block Index 究竟有多大?{offset : Long 类型,size:int 类型,firstKey},假设用户 的rowKey长度是50byte,一个64KB的DataBlock需要大约62个字节(50+8+4)来存储。假设一个Region Server中有500个HRegion,每个Region大小为10G(假设全部都是DataBlock),那么大约有(500Region*10G*1024M*1024KB/HFile大小64KB) 8192W个HFile。那Data Block Index的 大小为(81920000*62个字节)约为4.7G的大小。

HFileV2.0版本

image

文件分4个部分:

1、scanned block section :表示顺序扫描HFile的时候,所有的数据块都会被读取,同时包含leaf index block和bloom block。

2、non-scanned block section:表示在顺序扫描的时候,不会被读取的数据,包含meta block和 intermediate Levevl Data Index Blocks。

3、load-on-opensetion:在HRegion Server启动的时候,需要加载到内存的数据包含。FileInfo,Bloom Filter metadata,MetaIndex,RootDataIndex,Fields for midkey,metaIndex.

4、Trailer :主要记录了HFile的基本信息,各段的偏移量和寻址信息

设计要点:

1、分层索引:无论是Data Block Index 和 Bloom Filter 都做了分层索引的设计,V1版本只有简单的一级索引,Data Block 在V2版本的索引可以支持到三级,最底层的是leaf index block 可以 直接索引到Data Block ,中间层是intermediate Levevl Data Index Blocks 上层是Root Data Index。Root Data Index 在 load-onopen-setion中,随着启动加载。基本的逻辑是Root索引->intermed索引->leaf index 索引->data block。在实际过程中可能经常性不会用到中间层的索引,所以只需要2次索引查询。 同时Bloom Filter 被拆分为了多个Bloom block,在 load-onopen-setion  同样存储了Bloom Filter的索引。

2、交叉存储:在scanned block section  存储Data Block(用户的kV数据),leaf index block (Data block索引)和Bloom block 交叉存储。

3、按需索取:无论是Data block的索引数据,还是Bloom Filter的数据,都被拆分为了多个block,基于这样的设计,无论是索引数据还是Bloom Filter的数据,都可以按需存取,避免一次性都加载到HRegion Server中消耗大量内存。

V3版本的HFile相对于V2增加了Cell TAG属性,同时增加Key 存放在了block的起始位置,value放在了block的结束位置,两个采用不同的算法。

HFile的生成流程

       1、起初HFile中没有任何数据,当memStore存满了以后,发生Flush时,创建HFile Writer,第一个空的Data Block出现。初始化后的Data Block为为Header预留了空间呢,Header 将来会存放Block的一些元数据信息。然后位于MemStore中的keyvalues被一个一个的append到第一个data block中。

注:如果配置了Data Block Encoding,会在append Value的时候,同时进行编码,编码后的数据不再是单纯的keyvalue形式。

2、当一个DataBlock被写满了以后(默认是64KB),一个DataBlock将停止写入,该 Data Block将执行以下步骤。

1)如果有启动压缩或者加密特性,对Data Block中数据按照相应的算法进行压缩和加密。

2)在预留的header区域中,写入该Data Block的元数据信息,包含(压缩前大小,压缩后大小,上一个block的偏移信息,checkSum元数据信息)

3)生成checksum信息

4)Data Block以及check Sum信息通过HFile Writer的输出流写入到HDFS中。

5)为输出的Data Block生成一条索引记录,包含这个Data Block的(起始Key,偏移,大小)等信息,这条索引记录被暂时记录到内存中的block index chunk中:

注:上图中的firstKey 并不一定是Data Block的第一个Key,有可能是上一个Data Block的最后一个Key与这一个Data Block的第一个Key之间的一个中间值。       

3、至此,已经写入了第一个Data Block,并且在block Index Chunk中能记录了关于这个Data Block的一条索引信息。

4、随着Data Block的不断增多,Block Index Chunk中记录的数据也在不断增加,当Block Index Chunk增加到一定大小以后(默认是128K),Block index chunk 与Data Block采用类似的处理流程,输出到HDFS中,形成第一个Leaf Index   Block。

5、此时已输出的 Scanned Block 的部分构成如下

6、正是因为Leaf Index Block和DataBlock的交叉存在,Leaf index block 也被称为inline block(Bloom block 也属于inline block)。内存中还有一个Root Block Index Chunk 用来记录每一个leaf index block的索引信息。

7、先假设没有Bloom Filter的数据,当memStore的keyvalue数据全部写完以后,HFile Write 开始在Close方法中进行最后的收尾工作:

1)写入最后一个Data Block

2)写入最后一个Leaf index block。

3)如果有metaData 则写入non_scanned Block setionn区域。

4)写Root block index chunk 部分数据:如果Root Block index chunk数据超出了预设大小,则输出位于non_scanned block section 区域的intermediate inndex block 数据,以及生成并输出Root Index block到load-on-opennn-sectionn区域,如果没有超过大小,则直接输入到load-on-open-section区域。

5)写入用来索引Meta Block的Meta Index数据(事实上是写入一个空的block)

6)写入File Info信息,FileInfo信息包含。MaxSequenceID,Major Compactionn标记,Time Ranage信息,最早的Timestamp,DataBlock Encoding类型,Bloom Filter配置,最大的timestamp,keyvalue版本,最后一个rowKey,平均Key长度。

7)写入Bloom Filter的元数据与索引信息

注:前面的每一部分写入,都是以block的形式进行写入,都包含header和data部分,header的结构也是相同的,只是都有不同的block type。在data部分,每一种你那个类型的block都可以有自己的定义。

8)写入Trailer部分信息,包含:Root Index Block的offset,FileInfo的Offset,Data Block Index的层级,Data Block Index的数据总大小,第一个Data Block的offset,最后一个Data Block的offset,Comparator信息,Root Index Block的Entries数量,加密算法类型,Meta Index Block的Enntries数量,整个HFile未压缩大小,整个HFile的KeyValue总数,压缩算法类型。

Bloom Filter

上图中没有描述Bloom Filter中的内容,Blom Filter用来快速判断一条记录是否在一个大的集合中存在,采用多个Hash+位图的设计。写入数据时,一条记录经过X次Hash函数运算后,被运算到位图中的X个位置。判断一条记录是否存在时,也是将这条记录经过X次Hash计算,判断位图中的X位是否为1,表示记录可能存在,但是如果为0,就一定不存在。

Bloom Filter包含Bloom 的元数据信息(Hash 函数类型,Hash函数个数)与位图数据(Bloom Data),为了避免每次读取加载所有的Bloom Data ,在HFile V2版本中将Bloom Data 拆分为了多个Block。Bloom Data的类型也被称为Inline block。而关于Bloom Filter的元数据与多个Bloom Block的索引信息,被存放在Load-On-Open Section部分。但需要注意的是,在FileInfo部分,保存了关于BloomFilter配置类型信息,共包含三种类型:不启用,基于Row构建BloomFilter,基于Row+Column构建Bloom Filter。

DataBlock

        Data Block 是HBase中最小的存储单元(默认是64kb),Data Block主要用于存储用户的KV数据,每个数据都是以KV的结构在Hbase中存储,如下图所示:

每个KeyValue都由4部分组成,可以看出Key是一个复杂的结构,包含你多个属性。首先是rowkey的长度,接着是rowkey,然后是ColumnFamily的长度,再是ColumnFamily,之后是ColumnQualifier,最后是时间戳和KeyType(keytype有四种类型,分别是Put、Delete、 DeleteColumn和DeleteFamily),value就没有那么复杂,就是一串纯粹的二进制数据。

HMaster节点

  • 管理HRegionServer,实现其负载均衡;
  • 管理和分配HRegion,比如在HRegion split时分配新的HRegion;在HRegionServer退出时迁移其内的HRegion到其他HRegionServer上;
  • 实现DDL操作(Data Definition Language,namespace和table的增删改,column familiy的增删改等);
  • 管理namespace和table的元数据(实际存储在HDFS上);
  • 权限控制(ACL);

        HMaster没有单点故障问题,可以启动多个HMaster,通过ZooKeeper的Master Election机制保证同时只有一个HMaster处于Active状态,其他的HMaster则处于热备份状态。一般情况下会启动两个HMaster,非Active的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。前文已经介绍过了HMaster的主要用于HRegion的分配和管理,DDL(Data Definition Language,既Table的新建、删除、修改等)的实现等,既它主要有两方面的职责:

3.1、协调HRegionServer

        启动时HRegion的分配,以及负载均衡和修复时HRegion的重新分配;
        监控集群中所有HRegionServer的状态(通过Heartbeat和监听ZooKeeper中的状态);

3.2、Admin职能

  创建、删除、修改Table的定义;

HBase  查询

Hbase在0.96版本去掉了Root表,只剩下了特殊的 meta table表,它存储了集群中所有用户Region的位置,而Zk节点(hbase/meta-region-server)存储的直接是这个meta table的位置,同样这个Region表是不可分割的,这样Client访问的流程就变成了。

1)从zk中的hbase/meta-region-server获取meta table的位置,然后缓存起来。

2)从meta table表中查询用户table,对应请求的rowkey在哪个HRegionServer中,缓存RowKey对应的Region信息

3)从查询到HRegion Server中再进行查询,获取到具体的信息。

从以上可以看出,Client会缓存这些位置信息,随着时间推移,查询ZK和meta table的次数会越来越少,当Region 发生拆分的时候,需要重新刷新缓存。

hbase:meta表

Hbase meta 表存储了所有的Region的信息,它的row key 是 tableName,Regio StartKey,regionId,Region replicaId等,它只有Info列簇,info列簇包含:

1)info:regioninfo列 ,是RegionInfo的proto格式:  regionId,tableName,startKey,endKey,offline,split,replicaId;

2)info:server格式:HRegionServer对应的server:port;

3)info:serverstartcode格式是HRegionServer的启动时间戳。

HRegionServer详解

            HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性。HRegionServer包含多个HRegion,由WAL(HLog)、BlockCache、MemStore、HFile组成。

          1)WAL即Write Ahead Log,HBase 1.0之后进行了改进成了多级写入,解决性能写入问题,用来进行在灾难恢复。

 2)Block Cache是读缓存,“引用局部性原理”,(分空间局部性和时间局部性,空间局部性是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指某个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问)HBase中提供两种BlockCache的实现:默认on-heap LruBlockCache和BucketCache(通常是off-heap)。通常BucketCache的性能要差于LruBlockCache,然而由于GC的影响,LruBlockCache的延迟会变的不稳定,而BucketCache由于是自己管理BlockCache,而不需要GC,因而它的延迟通常比较稳定,这也是有些时候需要选用BucketCache的原因。

       两个图表示一下:

HRegionServer中数据写流程

       1) 当客户端发起一个Put请求时,首先它从hbase:meta表中查出该Put数据最终需要去的HRegionServer。然后客户端将Put请求发送给相应的HRegionServer,在HRegionServer中它首先会将该Put操作写入WAL日志文件中(Flush到磁盘中)。

2)写完WAL日志文件后,HRegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。此时写成功,并返回通知客户端。 ACK (Acknowledgement)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。

3)MemStore Flush,MemStore是一个In Memory Sorted Buffer,在每个HStore中都有一个MemStore,即它是一个HRegion的一个Column Family对应一个实例。它的排列顺序以RowKey、Column Family、Column的顺序以及Timestamp的倒序,如下所示:4

4) 每一次Put/Delete请求都是先写入到MemStore中,当MemStore满后会Flush成一个新的StoreFile(底层实现是HFile),即一个HStore(Column Family)可以有0个或多个StoreFile(HFile)。有以下三种情况可以触发MemStore的Flush动作,需要注意的是MemStore的最小Flush单元是HRegion而不是单个MemStore。据说这是Column Family有个数限制的其中一个原因,估计是因为太多的Column Family一起Flush会引起性能问题?具体原因有待考证。

HBase读的实现

        通过前文的描述,我们知道在HBase写时,相同Cell(RowKey/ColumnFamily/Column相同)并不保证在一起,甚至删除一个Cell也只是写入一个新的Cell,它含有Delete标记,而不一定将一个Cell真正删除了,因而这就引起了一个问题,如何实现读的问题?要解决这个问题,我们先来分析一下相同的Cell可能存在的位置:首先对新写入的Cell,它会存在于MemStore中;然后对之前已经Flush到HDFS中的Cell,它会存在于某个或某些StoreFile(HFile)中;最后,对刚读取过的Cell,它可能存在于BlockCache中。既然相同的Cell可能存储在三个地方,在读取的时候只需要扫瞄这三个地方,然后将结果合并即可(Merge Read),在HBase中扫瞄的顺序依次是:BlockCache、MemStore、StoreFile(HFile)。其中StoreFile的扫瞄先会使用Bloom Filter过滤那些不可能符合条件的HFile,然后使用Block Index快速定位Cell,并将其加载到BlockCache中,然后从BlockCache中读取。我们知道一个HStore可能存在多个StoreFile(HFile),此时需要扫瞄多个HFile,如果HFile过多又是会引起性能问题。

Compaction压缩:

        MemStore每次Flush会创建新的HFile,而过多的HFile会引起读的性能问题,那么如何解决这个问题呢?HBase采用Compaction机制来解决这个问题,有点类似Java中的GC机制,起初Java不停的申请内存而不释放,增加性能,然而天下没有免费的午餐,最终我们还是要在某个条件下去收集垃圾,很多时候需要Stop-The-World,这种Stop-The-World有些时候也会引起很大的问题。在HBase中Compaction分为两种:Minor Compaction和Major Compaction。

  Minor Compaction是指选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。一次Minor Compaction的结果是更少并且更大的StoreFile。

 Major Compaction是指将所有的StoreFile合并成一个StoreFile,在这个过程中,标记为Deleted的Cell会被删除,而那些已经Expired的Cell会被丢弃,那些已经超过最多版本数的Cell会被丢弃。一次Major Compaction的结果是一个HStore只有一个StoreFile存在。Major Compaction可以手动或自动触发,然而由于它会引起很多的IO操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间。

HRegion Split

最初,一个Table只有一个HRegion,随着数据写入增加,如果一个HRegion到达一定的大小,就需要Split成两个HRegion,这个大小由hbase.hregion.max.filesize指定,默认为10GB。当split时,两个新的HRegion会在同一个HRegionServer中创建,它们各自包含父HRegion一半的数据,当Split完成后,父HRegion会下线,而新的两个子HRegion会向HMaster注册上线,处于负载均衡的考虑,这两个新的HRegion可能会被HMaster分配到其他的HRegionServer中。关于Split的详细信息,可以参考这篇文章:《Apache HBase Region Splitting and Merging》。

HRegion负载均衡

        在HRegion Split后,两个新的HRegion最初会和之前的父HRegion在相同的HRegionServer上,出于负载均衡的考虑,HMaster可能会将其中的一个甚至两个重新分配的其他的HRegionServer中,此时会引起有些HRegionServer处理的数据在其他节点上,直到下一次Major Compaction将数据从远端的节点移动到本地节点。

HRegionServer Recovery灾难恢复:

当一台HRegionServer宕机时,由于它不再发送Heartbeat给ZooKeeper而被监测到,此时ZooKeeper会通知HMaster,HMaster会检测到哪台HRegionServer宕机,它将宕机的HRegionServer中的HRegion重新分配给其他的HRegionServer,同时HMaster会把宕机的HRegionServer相关的WAL拆分分配给相应的HRegionServer(将拆分出的WAL文件写入对应的目的HRegionServer的WAL目录中,并写入对应的DataNode中),从而这些HRegionServer可以Replay分到的WAL来重建MemStore。

 

HLog 

  HLog(WAL log):WAL意为write ahead log,用来做灾难恢复使用,HLog记录数据的所有变更,一旦region server 宕机,就可以从log中进行恢复。 

LogFlusher 

  定期的将缓存中信息写入到日志文件中 

LogRoller  

   对日志文件进行管理维护

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值