HBase是一个开源的非关系型分布式数据库(NoSQL),是Hadoop项目的一部分,运行于HDFS文件系统之上, (Hadoop Database )。主要应用场景是实时随机读写超大规模的数据。java
HBase 内部管理的文件所有都是存储在 HDFS 当中的 ,由于是分布式的存储,因此表都是逻辑上的结构,物理上的存储分布在多个RegionServer上,而且须要一个主节点HMaster进行控制mysql
**Region是分布式存储的最小单元,存储在实际机器上,一个 Region 是由一个或多个 Store 组成。**每个 Store 其实就是一个列族。每一个Store 又是由一个 memStore 和 0 个或者多个 storeFile 组成。memStore 是存储在内存中,storeFile 是存储在 HDFS 中,有时候也称做 HFile。数据都会先写入memStore,一旦 memStore 超过给的的最大值以后,HBase 就会将memStore 持久化为 storeFile。算法
HBase架构中只有一个Master节点,称HMaster,还有多台RegionServer成为HRegionServer,每一个RegionServer包含多个Region。sql
client访问hbase上数据的过程并不须要Master参与(寻址访问Zookeeper和RegionServer,数据读写访问RegionServer),Master仅仅维护Table和Region的元数据信息,负载很低。shell
RegionServer主要管理两种文件,一是Hlog(预写日志 write-ahead log WAL),二是region(实际的数据文件)。当用户向hbase请求写入put数据时,首先就要写入Hlog实现的预写日志当中,当hbase服务器崩溃时,hlog能够回滚尚未持久化的数据,以便后续从WAL中恢复数据。 当数据写入预写日志后,数据便会存放当region中的memStore中,同时还会检查memStore是否写满,若是写满,就会被请求刷写到磁盘中。数据库
通常步骤:vim
1.联系zookeeper,找出meta表(元数据表)所在rs(regionserver) /hbase/meta-region-server
数组
2.定位row key,找到对应region server 3.缓存信息在本地(再次查找数据时,不须要通过1,2步骤)缓存
4.联系RegionServerbash
5.HRegionServer负责open HRegion对象,为每一个列族建立Store对象,Store包含多个StoreFile实例,他们是对HFile的轻量级封装。每一个Store还对应了一个MemStore,用于内存存储数据。
HDFS构建了一个分布式文件系统,HBase则是在其基础之上构建一个非关系型数据库,HBase的全部操做最终仍是会基于HDFS,映射到HDFS之上,全部逻辑上的操做都是在HDFS之上进行。
配置hbase-site.xml
位于 ${HBase-Dir}/conf/hbase-site.xml
单击版配置:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>file:///tmp/hbase-${user.name}/hbase</value>
</property>
</configuration>
复制代码
伪分布式配置:
<property>
<name>hbase.rootdir</name>
<value>hdfs://localhost:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
</configuration>
复制代码
hbase.rootdir:该参数制定了HReion服务器的位置,即数据存放的位置。主要端口号要和Hadoop相应配置一致。
hbase.cluster.distributed:HBase的运行模式。false是单机模式,true是分布式模式。若为false, HBase和Zookeeper会运行在同一个JVM里面。默认为false.
设置环境变量
修改HBase下的conf目录中的hbase-env.sh文件
export JAVA_HOME=/usr/lib/jvm/java-8-oracle
export HBASE_MANAGES_ZK=true
复制代码
为方便使用,将HBase下的bin命令放在系统的/etc/profile下:
sudo vim /etc/profile
# set hbase path
export HBASE_HOME=/opt/hbase-1.2.6
export PATH=$PATH:/opt/hbase-1.2.6/bin
复制代码
在hbase的bin下执行脚本
./start-hbase.sh
./stop-hbase.sh
复制代码
HBase基于HDFS,所以必须先启动HDFS
启动HBase实例以后,使用 ./hbase shell链接HBase实例到shell
命令 quit
退出HBase Shell 而且断开和集群的链接
将其当作管理多个表的一个Schema便可,如同mysql中的database
create_namespace 'test'
复制代码
默认状况下,是在系统的namespace下建表,在自建的namepace下建表使用namespace名:表名
status:查看HBase状态
create:建表,指定表名与列族名,全部字段都要用' '包含起来,第一个为表名,剩余都是列族名
hbase(main):001:0> create 'test', 'cf'
复制代码
hbase(main):002:0> list 'test'
复制代码
disable 'test'
drop 'test'
复制代码
exists 'test'
复制代码
put 'test', 'row1', 'cf:a', 'yasuo'
put 'test', 'row2', 'cf:b', 'lsl'
复制代码
重复put列会直接覆盖
get 'test', 'row'
复制代码
scan 'test'
复制代码
在设计rowkey时,有大量连续编号的rowkey。这就会致使大量rowkey相近的记录集中在个别region里,也就是集中在一台或几台regionServer当中。当client检索记录时,对个别region访问过多,这时此region所在的主机过载,就致使热点问题的出现。 也就是说大量访问集群中的某一台PC致使该PC过载,致使负载不均衡
解决方法:
加盐
在rowkey的前面分配随机数,当给rowkey随机前缀后,它就能分布到不一样的region中,这里的前缀应该和你想要数据分散的不一样的region的数量有关。
引用例子: www.shiyanlou.com/courses/37/…
咱们去电信公司打印电话详单也就是通话记录。对于通话记录来讲,每一个人每个月可能都有不少通话记录,而使用电信的用户也是亿计。这种信息,咱们就能存入hbase当中。
对于通话记录,咱们有什么信息须要保存呢?首先,确定应该有主叫和被叫,而后有主叫被叫之间的通话时长,以及通话时间。除此以外,还应该有主叫的位置信息,和被叫的位置信息。
由此,咱们的通话记录表须要记录的信息就出来了:主叫、被叫、时长、时间、主叫位置、被叫位置。
咱们该如何来设计一张hbase表呢?
首先,hbase表是依靠rowkey来定位的,咱们应该将尽量多的将查询的信息编入rowkey当中。hbase的元数据表mate表就给咱们了一个很好的示例。它包括了namespace,表名,startKey,时间戳,计算出来的码(用于分散数据)。
因此,当咱们设计通话记录的rowkey时,须要将能惟一肯定该条记录的数据编入rowkey当中。便是须要将主叫、被叫、时间编入。
以下所示:
17765657979 18688887777 201806121502 #主叫,被叫,时间 复制代码
可是咱们可否将咱们设计的rowkey真正应用呢?
固然是能够的,可是热点问题便会随之而来。
例如你的电话是以177开头,电信的hbase集群有500台,你的数据就只可能被存入一台或者两台机器的region当中,当你须要打印本身的通话记录时,就只有一台机器为你服务。而如果你的数据均匀分散到500机器中,就是整个集群为你服务。二者之间效率速度差了不止一个数量级。
注意:因为咱们的regionServer就只有一台,没有集群环境,因此咱们只介绍方法和理论操做,不提供实际结果 复制代码
由于咱们设定整个hbase集群有500台,因此咱们随机在0-499之间中随机数字,添加到rowkey首部。
以下所示:
12 17765657979 18688887777 201806121502 #随机数,主叫,被叫,时间 复制代码
在插入数据时,判断首部随机数字,选择对应的region存入,因为rowkey首部数字随机,因此数据也将随机分布到不一样的regionServer中。这样就能很好的避免热点问题了。`
对于大量重复数据须要统计的时候,利用hash的性质进行一次预处理,将hashcode相同的数据交给同一台PC,这样PC能进行快速计算同时避免进一步合并,即不是将数据均分,而是依据已有数据的性质作一次运算,根据结果分配数据以达到算力分配最优。
引用例子: www.shiyanlou.com/courses/37/…
例如,咱们有一个10TB的大文件存在分布式文件系统上,存的是100亿行字符串,而且字符串无序排列,如今咱们要统计该文件中重复的字符串。
假设,咱们能够调用100台机器来计算该文件。
那么,如今咱们须要怎样经过哈希函数来统计重复字符串呢。
首先,咱们须要将这一百台机器分别从0-99标好号,而后咱们在分布式文件系统中一行行读取文件(多台机器并行读取),经过哈希函数计算hashcode,将计算出的hashcode模以100,根据模出来的值,将该行存入对应的机器中。
根据哈希函数的性质,咱们很容易看出,相同的字符串会存入相同的机器中。
而后咱们就能并行100台机器,各自分别计算相应的数据,大大加加快统计的速度。
注意:这10TB文件并非均分红100GB,分给100台机器,而是这10TB文件中不一样字符串的种类,均分到100台机器中。若是还嫌单个机器处理的数据过大,能够按照一样的方法,在一台机器中并行多个进程,处理数据。 复制代码
布隆过滤器是一种利用hash算法节约空间的存储方式,最基本的以每个位(bit)为基本单位,bit是信号量,有0,1两种状态,所以能够根据须要过滤某些数据(如IP黑名单),直接存ip过于耗费内存,所以不存ip,而是根据hash进行映射得到hashcode,根据hashcode对信号量的位置取模变换为0或1,即以时间换空间。但由于基于hash,可能两个不一样的数据获得同一个hashcode,所以可能会出现数据误判状况。
引用例子: www.shiyanlou.com/courses/37/…
首先,咱们应当知道,hash是内存中使用的经典数据结构。
当咱们须要判读一个元素是否在一个集合当中时,咱们能够用哈希表来判断。在集合较小的状况下,hash是可行并且高效的。
然而数据量以PT计的大数据场景中,不少时候,hash便力有未逮。这是由于在海量数据下hash要占据巨额内存空间,这远远超过咱们可以提供的内存大小。
例如在黑名单过滤当中,咱们有100亿的网站黑名单url须要过滤,假设一个url是64bytes。若是咱们用hash表来作,那么咱们至少须要6400亿字节即640G的内存空间(实际所需空间还远大于此),空间消耗巨大,必需要多个服务器来同时分摊内存。
然而咱们是否能用更加精简的结构来作这件事呢?布隆过滤器就是这样一个高度节省空间的结构,而且其时间也远超通常算法,可是布隆过滤器存在必定的失误率,例如在网页URL黑名单过滤中,布隆过滤器毫不会将黑名单中网页查错,可是有可能将正常的网页URL断定为黑名单当中的,它的失误能够说是宁肯错杀,不可放过。不过布隆过滤器的失误率能够调节,下面咱们会详细介绍。
布隆过滤器实际就是一种集合。假设咱们有一个数组,它的长度为L,从0-(L-1)位置上,存储的不是一个字符串或者整数,而是一个bit,这意味它的取值只能为0或1.
例如咱们实现以下的一个数组:
int[] array = new int[1000]; 复制代码
该数组中有1000个int类型的元素,而每个int由有4个byte组成,一个byte又由8个bit组成,因此一个int就由32个bit所组成。
因此咱们申请含1000整数类型的数组,它就包含32000个bit位。
可是咱们若是想将第20001个bit位描黑,将其改成1,咱们须要怎样作呢。
首先咱们的须要定位,这第20001个bit位于哪一个整数,接着咱们须要定位该bit位于该整数的第几个bit位。
int index = 20001; int intIndex = index/32; int bitIndex = index%32; 复制代码
而后咱们就将其描黑:
array[intIndex] =(array[intIndex] | (1 << bitIndex)); 复制代码
这段代码可能有些同窗不能理解,下面咱们详细解释一下。 咱们的数组是0-999的整型数组,可是每一个位置上实际保存的是32个bit,咱们的intIndex就是表明第几个整数,定位到625,而bitIndex定位到第625个整数中的第几个bit,为1。
因此,咱们须要描黑的位置是第625号元素的第1个bit。
而
(1 << bitIndex)
表明将1左移到1位置,便是表明只有1位置为1,而其余位置为零。就至关于得到了这样一串二进制数:00000000 00000000 00000000 00000001 复制代码
而后咱们将这样的二进制数与array[indIndex]进行逻辑或运算,就能将第625号元素的第一个bit描黑变1,最后咱们将描黑后的元素赋值给array[indIndex],完成运算。
这里咱们采用的是int类型的数组,若是咱们想更加节省空间,咱们就能建立long类型的数组,这样申请1000个数组空间,咱们就能获得64000个bit。
而后咱们还能进一步扩展,将数组作成矩阵:
long[][] map = new long[1000][1000]; 复制代码
有了这些基础之后,咱们如何设计黑名单问题呢?
假设咱们已经有了一个拥有m个bit的数组,而后咱们将一个URL经过哈希函数计算出hashcode,而后
hashcode%m
将对应位置描黑。然而这还不是真正的布隆过滤器,真正的布隆过滤器是经过多个哈希函数对一个URL进行计算,获得hashcode,而后在对不一样位置的bit进行描黑。
注意:布隆过滤器采用的多个哈希函数必须是相互独立的,前面咱们已经介绍了如何经过一个哈希函数构造多个独立哈希函数的方法。 复制代码
当咱们将一个URL经过n个哈希函数,获得hashcode,模以m,再将对应位置描黑以后。咱们能够说该URL已经进入咱们的布隆过滤器当中了。
接下来,咱们将黑名单中全部URL都经过哈希函数,进入布隆过滤器中(布隆过滤器并不真正存储实际的URL)。
对于一个新的URL,咱们要查询其是否在黑名单中,咱们便经过一样的n个哈希函数,计算出n个位置,而后咱们查询这n个位置是否都被描黑,若是都被描黑,咱们就说该URL在黑名单当中。若是n个位置但凡是有一个不为黑,咱们就说该URL不在该黑名单中。
注意:咱们的数组不该该太小,否则极可能数组中的大多数位置都被描黑,这很容易将正常的URL断定为不合法的,这也是布隆过滤器的失误率来源。 复制代码
参考:
实验楼
知乎
<<Hadoop权威指南 第四版>>