作者:songzeng, 腾讯IEG增长中台 后台开发工程师
大数据概况
大数据的定义
研究机构Gartner:
“大数据”是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力来适应海量、高增长率和多样化的信息资产。
麦肯锡全球研究所:
一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合,具有海量的数据规模、快速的数据流转、多样的数据类型和价值密度低四大特征。
IBM提出大数据的5V特点:
Volume(大量)、Velocity(高速)、Variety(多样)、Value(低价值密度)、Veracity(真实性)。
大数据就犹如是河边快速流动、变化莫测、无穷无尽的沙石,你知道里面肯定存在矿石,但是想要提取出来并不容易,需要掌握一定的技巧以及一次次的提炼。
大数据应用场景
大数据的实际应用其实古今中外都有用到,并不一定是现代的特有。
古有孙膑减灶,马陵之战中,孙膑抓住对手庞涓善于进行数据分析的特点,有意[使齐军入魏地为十万灶,明日为五万灶,又明日为三万灶],通过反其道而行之,终对庞涓成功实施诱杀。
现如今,大数据不只在互联网,同时也在各行各业都占据了重要的位置。比如交通堵车的预测,农牧虫害的防治等等。
大数据技术图谱
大数据整个处理流程中包含非常之多的技术,以下列出常见的一些技术点:
数据采集
Sqoop、Flume、Logstash、Kibana
数据存储
HDFS、Ceph
数据仓库
Hbase、 MongoDB、Cassandra、Neo4j、Redis
数据计算
Hadoop MapReduce、Spark、Storm、Flink
数据分析
Hive、Pig、Impala、Presto、Kylin、Druid
资源管理
Mesos、Yarn
集群管理与监控
Ambari、Ganglia、Nagios、Cloudera Manager
机器学习
Mahout、Spark MLLib
其它
Kafka、StormMQ、Zookeeper
以上标红的技术点,本文章会主要描述。
常用六大组件
以下介绍的六大组件包含了数据存储、数据计算、数据分析、资源调度、数据仓库,基本上包含了一般大数据处理的整个流程。
Hadoop
定义:Hadoop是Apache旗下的一个用java语言实现开源软件框架,是一个开发和运行处理大规模数据的软件平台。
主要解决:海量数据的存储和海量数据的分析计算问题
我们下面通过一个简单的例子来更加形象的了解Hadoop的全貌。
现在有这么一个场景:统计10亿个单词的每个单词出现次数,你会怎么做?
1、单机遍历
2、多线程遍历
3、手动切分给不同机器进行统计再人工汇总
首先单机会很慢,其次多线程编码复杂而且也有瓶颈,第三种方案虽然可以并行处理但是切分给不同机器手动操作也非常麻烦。
那会不会有更加自动化的方案呢?就是那种又快又简单方便的。
hadoop其实就第三种方案的自动化实现,通过分布式解决了单机的瓶颈,通过框架本身解决了需要手动拆分和聚合,我们只需要在hadoop平台上像编写单机程序一样编写分布式程序,所以有了hadoop之后,这类场景计算将会非常简单。所以,hadoop在大数据领域的地位非常之高。
Hadoop发展到了现在已经不单单指Hadoop这个框架,从广义上来说hadoop指的是一个更广泛的概念——hadoop生态圈;在解决大数据问题的过程中hadoop发展和引入了一批优秀的周边软件,比如解决系统的高可用引入了zookeeper,比如解决如何更加高效的使用hadoop发展出了Hive,这就像是一场进化,随着网络的升级,硬件的成本降低,大数据处理也不断的出现瓶颈,同时也不断的涌现出一批优秀的解决方案,这些方案最终的实现构成了hadoop整个生态圈。
Hadoop 发展历史
2002年当时第一轮互联网泡沫刚刚破灭, 一个对搜索引擎特别了解但是同时又失去工作的青年Doug Cutting(道格·卡丁)觉得 反正没啥事干,于是打算搞一个开源的搜索引擎出来, 说干就干, 就干了个项目叫Nutch。
花了两年时候终于做完了,结果很不巧, 在那个时候, Google公布了著名的GFS和MapReduce两篇论文。这哥们一看, 完了, 这两年白干了, 人家干得那才是漂亮, 自己现在干得实在是太苦逼了, 所有工作都处于人肉运维的状态。那咋办呢, 重构呗, 咋重构啊, 抄一个呗。于是就开始借鉴GFS和MapReduce, Google用的C++做, 他们用的Java做。
2006以后,Dog Cutting跟好多我们同龄人一样, 做了几年公司, 发现干也干不过google了, 好像创业没啥前途了, 那咋办呢, 就找个大公司吧。这哥们本来一开始想去IBM, 但是人家IBM要他很早前的一个搜索引擎项目lucene, 不要他的Nutch。这哥们表示不开心, 就去问Yahoo愿不愿意要Nutch, 人家Yahoo有自己的搜索引擎, 也不愿意要Nutch。不过Yahoo考虑了一下, 说虽然不要你的搜索系统, 但是你底层那几个GFS/MapReduce那些东西还是挺有用的嘛, 要不你过来弄这个? Dog Cutting也就从了, 于是把底层系统剥离出来, 把自己儿子的一个大象的玩具的名字Hadoop赋予了这个项目。
至此,完成了hadoop的第一次进化。
Hadoop 组成
Hadoop最终目标就是解决大数据的存储和计算的问题,所以在最初版本时候主要是MapReduce和HDFS组成,MapReduce负责计算以及计算过程中的资源调度,HDFS负责数据存储。
因为计算引擎和资源调度耦合在一起,所以在后面出现了如Spark等更快的计算引擎需要替换的时候就变得非常麻烦,于是2.0版本的Hadoop把资源调度解耦了,也就是Yarn。
Hadoop 优势
- 高可靠性:Hadoop底层维护了多个数据副本,所以即使Hadoop某个计算元素或存储出现故障,也不会导致数据的丢失
- 高扩展性:能在廉价机器组成的集群间分配任务数据,可方便的扩展数以干计的节点
- 高效性:Hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快
- 高容错性:Hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配
hadoop的优势我们可以在了解完HDFS和MapReduce会感受的更加深刻。
HDFS
定义:Hadoop Distributed File System 的简称,是 Hadoop 抽象文件系统的一种实现,它是适合运行在通用硬件上的分布式文件系统
主要解决:大量的数据能横跨成百上千台机器,提供高吞吐量的服务,同时提供副本进行容错及高可靠性保证
HDFS核心目标除了存储大数据文件,还有就是流式数据访问,读取数据文件就像打开水龙头一样,可以不停的读取,因此HDFS对于读取整个数据集所需要的时间比读取随机一条数据所需要的时间更加重要,即HDFS更加重视数据的吞吐量,而不是数据的访问时间。
在我们使用 HDFS 时,用户看到的是一个文件系统而不是很多文件系统,比如说要获取 /root/file1.txt 的数据,引用的是一个文件路径,但是实际的数据存放在很多不同的机器上,作为用户,不需要知道这些,就好比在单机上我们不关心文件分散在什么磁道什么扇区一样,HDFS 为用户管理这些数据。
HDFS 系统架构
一个完整的HDFS文件系统通常运行在由网络连接在一起的一组计算机(或者叫节点)组成的集群之上,在这个集群之上运行着不同类型的守护进程,不同类型的守护进程相互配合、互相协作,共同为用户提供高效的分布式存储服务。
那么这些数据到底是存储在哪里呢?实际上HDFS会把文件切割成一个个的数据块,然后均匀的存放在集群中的数据节点中,那分别放在哪些节点呢?这个就是由一个主节点来管理的。
整个HDFS系统架构就如图中一样是一个主从架构。通常由一个NameNode和一个SecondaryNameNode以及至少一个DataNode组成。
NameNode是主从架构中的主节点,也称元数据节点,它管理文件系统的命名空间,维护着整个文件系统的目录树以及目录树中的所有子目录和文件。
Secondary NameNode是HDFS主从架构中的备用节点,因为Namenode的元数据是保存在内存中,所以在NameNode挂了之后辅助其快速恢复。
DataNode是主从架构的从节点,也称数据节点,HDFS上的文件都是以文件块的形式组成,每个块默认128M(hadoop2.X版本),所有这些块都分布在DataNode节点上,其实对于单台DataNode来说,数据块就是一个普通文件,DataNode接受NameNode对于文件读写、删除、移动等指令。也就是说NameNode知道整个集群文件块的位置,而DataNode只能知道自己节点上的文件位置。
HDFS 数据节点
这张图表示有两个机架,每个机架有三个数据节点,里面在三个数据节点分别有一个数据块。
这里我们这里思考两个问题:
- 节点的大小设置是基于什么原则呢?
前面我们说过每个节点默认是128M,和普通磁盘数据块设置的大小比这明显偏大,之所以这么大是因为需要减少寻址的开销,如果设置的足够大,那一个文件的数据块就会更少,那么寻址时间就会明显会小于数据传输时间,那是否越大越好呢?那也不是,因为这些数据需要给上层计算使用,如果单个数据块太大会影响计算的整体时间。
默认有个原则就是,假如寻址时间是10ms,磁盘传输速度是100M/s,那么数据块的大小可以设置为100M。
- 数据副本备份策略是什么?
前面说到HDFS是建立在廉价机器上的集群,所以节点的不可用对于HDFS来说是当场常态来处理而不是异常。所以HDFS默认会对每个数据块保存三份,第一份随机保存在一个节点上,第二三份保存同一个机架的不同节点上,但是不会是第一份副本的机架上,这样做的目的是,考虑到不同机架同时出错的概率更低,那为什么不放在三个不同的机架呢,因为这样会加大同步的时间,因为同一个机架的距离短同步时间也非常快。
HDFS 写数据流程
第一步:客户端会通过RPC和NameNode也就是元数据节点进行通信,建立完成后把文件路径传给NameNode请求上传文件。
第二步:Namenode执行检查,查看文件是否已经存在,以及客户端是否有新建权限,通过检查则创建一个文件的元数据,否则抛出异常,
第三步:客户端会对文件进行切割成每个128M的数据块,然后向NameNode请求上传第一个数据块,NameNode收到后根据配置返回三个可用的dataNode列表;
第四步:客户端获得了数据节点列表,于是和离他最近的数据节点进行通信,然后把其他数据节点发送给这个节点,第一个节点会依次和下一个节点进行通信,最后形成一条完整通信链路。
第五步:客户端会对这个数据块再分为若干个数据包(这里和UDP传输类似),然后放入传输队列依次进行传输,第一个节点收到包之后进行处理并传给下一个节点,依次传输,然后每个节点依次向上应答,客户端收到应答则从传输队列去除这个包,如果失败则重试。
这里还有可能其中一个数据节点挂了,这时候会上报给NameNode,NameNode会进行恢复或者替换处理,保障集群的高可用。
HDFS 读数据流程
第一步:还是客户端会通过RPC和NameNode也就是元数据节点进行通信,然后请求读取指定文件
第二步:NameNode在内存中找到这个文件的所有数据块保存的相应节点地址,然后返回给客户端
第三步:客户端会和第一个数据块的最近的一个数据节点建立通信(最近原则通过DataNode上报机架信息计算得到)
第四步:数据节点以包为单位放入数据流进行传输
第五步:客户端接收数据流缓存在本地,然后和第二个数据块的节点进行通信,直到所有数据块都传输完成,再写入目标文件;
这里可能会遇到挂掉的数据节点,这时候客户端会上报给NameNode进行处理,然后自己会换另一个副本节点。
HDFS 优势
- 高可靠性:1)数据自动保存多个副本。它通过增加副本的形式,提高容错性 2)某一个副本丢失以后,它可以自动恢复
- 大数据文件:1)数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据 2)文件规模:能够处理百万规模以上的文件数量,数量相当之大
- 廉价硬件:构建在廉价机器上,通过多副本机制,提高可靠性
HDFS 劣势
通过前面的了解是不是觉得HDFS非常强大,无所不能,但其实因为设计目标的原因,它还是有一定的局限性。
- 不适合低延时数据访问,比如毫秒级的存储数据,是做不到的
- 无法高效的对大量小文件进行存储
- 不支持并发写入、文件随机修改
首先HDFS因为节点之间是通过网络来传输数据,所以很难做到毫秒级的数据访问
然后因为所有文件的元数据都保存在NameNode节点的内存中,这里的内存是一个瓶颈,所以HDFS不适合大量的小文件存储。虽然HDFS可以通过联邦机制来进行横向扩展,但是还是不能根本性解决小文件存储。
HDFS是通过文件追加的形式来写入数据,保障了数据的一致性,同时也就不支持文件的随机修改和并发写入了。
MapReduce
定义:Hadoop MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。
主要解决:将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。
Hadoop mapreduce 是一个运行在hadoop集群上的分布式运算程序框架。前面我们介绍Hadoop的时候有提到说,hadoop可以实现分布式计算程序和普通程序一样简单,这里主要就是MapReduce的功劳了,
他主要借助函数式编程以及分而治之的思想将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上
MapReduce 的基本设计思想
Mapreduce对大数据并行处理采用分而治之的设计思想,但是前提是这个大数据文件可以分成具有相同计算过程的多个数据块,并且相互之间不存在数据依赖关系,那么提高计算速度最好的方式就是采用分而治之的策略进行并行化计算。比如计算每天的曝光总数,消耗总数等等。
MapReduce 核心编程思想
我们已经知道mapreduce的分治并行计算的设计思想,那么我们再来看下具体是如何实现分治的。
如mapreduce的命名一般,他有两种类型的任务:Map和Reduce。
首先会将文件按照一定规则进行切分,然后所有Map任务负责从存储系统(如我们刚学的HDFS中)读取切割后的输入数据,按我们规则处理后写KV类型的结果数据到map节点的本地磁盘。
然后会有一个shuffle的过程会对多个map任务的输出进行合并、排序、输出结果,这样做是减少最终存储及传输的数据量,提高数据处理效率,shuffle执行完成之后把数据传入Reduce任务,reduce方法中封装了数据汇总的逻辑,对输入数据进行汇总处理,然后把汇总完的数据保存在指定的目录文件中。
MapReduce 优缺点
Mapreduce主要有以下优点:
第一:易于编程,在整个mapreduce过程中我们只需要编写map和reduce两个函数,其他的都可以交给框架本身处理。
第二:良好的扩展性,当集群的计算资源不能得到满足时,可以通过简单的添加机器来扩展集群的计算能力。
第三:高容错性,mapreduce初衷就是在廉价的商用服务器上运行计算,它把机器出错当场一种常态,所以设计了很高的容错性,比如一台机器出现故障,可以把上面的计算任务转移到另一个正常节点上,这个过程是自动完成的,不需要人工介入。
第四:适合PB级以上的海量数据的离线处理。
Mapreduce当然也不是全能的:
第一:它不擅长实时计算
第二:它不适合DAG(也就是也向无环图)计算,因为mapreduce每次计算结果都会写入到磁盘,如果任务之间存在依赖就只能去磁盘重新读取数据,这样会造成大量的磁盘IO,导致性能非常低下。
这样看来感觉mapreduce就像穷人家的孩子,用尽全身的能力在条件极差的背景下实现了伟大的目标,但是局限于自身的硬件条件,有些事情终究无法达成。而另一个离线计算引擎Spark则有点像一名富二代。
YARN
定义:Apache Hadoop YARN (Yet Another Resource Negotiator,另一种资源协调者)是一种新的 Hadoop 资源管理器,它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处
YARN 基本架构
这是一张yarn官网的架构图,我们可以看到主要有client, resource manager, node manager, container, applicationmaster,这几个模块都是干什么的呢?
权利的游戏大家应该都看过吧,我们用权力的游戏来解释一下这几个模块主要干什么的。
首先ResourceManager 就是全境守护者七国女王龙母,它主要是发号施令,自己本身是不干活的,主要负责处理客户端的请求以及全局资源的分配和调度。
NodeManager 就是维斯特洛大陆的七大王国,他们负责主要干活的,接收ResourceManager 的命令,同时负责自己本国资源的管理
ApplicationMaster就是某个事件的指挥部,比如诛杀夜王是由北境守护者负责,它负责诛杀夜王这个事情的资源申请和资源调度,它虽然是北境,但是也可以调度其他王国的资源,从上图也可以看到有两个ApplicationMaster,一个负责map任务,一个负责reduce任务,每个任务都会有一个ApplicationMaster。
Container就是一个逻辑模块,它代表将领、兵团、武器这些组成的战斗单位。在yarn里面就是代表内存、cpu、磁盘等资源的封装。
我们再来理一遍,有人发现僵尸入侵,于是报告给龙母,龙母把这个任务指派给了北境之王,北境之王把战斗任务分配到各个王国,各个王国组建自己的兵团进行战斗。
在yarn就是,客户端收到一个MapReduce的任务,于是向ResourceManager申请两个Application节点,ApplicationMaster则会把map和reduce任务分配到各个NodeManager节点,NodeManager节点获得任务会申请相应的资源创建一个Container来运行程序执行任务。
Hive
定义:Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射成一张表,并提供类SQL查询功能。
主要解决:将类SQL语句转变成MapReduce任务来执行。
Hive 设计思路
HDFS存储的数据是在文件中一条一条的结果数据,是没有表的概念的,而hive则通过给HDFS结构化的数据设计一张对应的表,给HDFS每条数据中按规则分成不同的列,再把这个对应关系也就是元数据保存在如mysql这样的关系型数据库中。
元数据包括:数据库信息、表名、表的列和分区及其属性,表的属性,表的数据所在的目录等。
这样hive就能实现类SQL的语句实现数据查询。
Hive的整个运行机制主要包含以下几步:
1、用户通过客户端接口连接Hive,发布HQL
2、Hive解析查询并制定查询计划
3、Hive将查询转换成mapreduce作业
4、hive在Hadoop上执行mapreduce作业
Hive 架构原理
这张图描述了hive的架构原理,元数据和客户端以及MapReduce我们已经讲过了,这里主要是补充了hive的最关键的驱动器实现。
驱动器主要实现步骤:
1、解析器将SQL字符串转换成抽象语法树AST
2、编译器将AST编译生成逻辑执行计划
3、优化器对逻辑执行计划进行优化
4、执行器把逻辑执行计划转换成可以运行的物理计划,对于Hive来说,就是MR/Spark
Hive 数据类型
Hive 支持非常丰富的数据类型,除了整型字符串浮点数时间类型等这些基础数据类型,hive还支持比较复杂的集合数据类型,结构体、map、数组。
案例讲解
需求:假设下面json结构需要存在HDFS,这个json有字符串有数据,有map,也有结构体,我们现在需要把这个数据存入hdfs并且用hive来映射出一张表,并使用Hive查询数据,如何实现?
{
"name":"laowang",
"friends":["lilei","hanmeimei"], // 列表array
"children":{ // 键值 Map
"xiao wang":18,
"xiao xiao wang":8
},
"address":{ // 结构Struct
"street":"tao yuan cun",
"city":"shenzhen"
}
}
操作流程:
步骤一:创建对应表存储(格式:, 隔开了每个字段,_隔开了每个字段中的多个值(数组和结构体),:隔开了map中的k和v)
create table test(
name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>
)
row format delimited fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '\n’;
步骤二:创建本地测试文件test.txt,按建表格式填入数据
laowang, lilei_hanmeimei, xiao wang:18, xiao xiao wang:8, tao yuan cun_shenzhen
步骤三:将数据上传到HDFS中,当前表对应的目录下
hive的方式:load data local inpath ‘./test.txt’ into table test;
hadoop方式:Hadoop fs -put ./test.txt /user/hive/test/test
步骤四:查询
select name, friends[0], childrends['xiao wang'], address.street from test;
Hive 优缺点
优点:
1、可以通过类sql语法来查询数据,提供快速开发的能力,上手简单
2、可以不用写mapreduce程序,减少了开发人员的学习成本
3、hive不仅仅可以和mapreduce和hdfs结合使用,同时也支持比如Hbase等等,具有良好的扩展性
局限性:
1、hive的HQL表达能力有限,也不支持迭代算法,有些复杂的运算不易表达。还是需要单独编写mapreduce来实现
2、由于hive是转换成mapreduce任务来运行的,所以hive的效率比较低、延迟高,并且hive的调优比较困难,调优需要考虑mapreduce,所以比较困难
HBase
定义:HBase–Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统
解决问题:HBase可以以低成本来存储海量的数据并且支持高并发随机写和实时查询
这里你可能会疑惑,既然是基于HDFS实现的,那他们两个到底什么关系呢?
首先HDFS是文件系统,而HBase是数据库,其实也没啥可比性。
「你可以把HBase当做是MySQL,把HDFS当做是硬盘。HBase只是一个NoSQL数据库,把数据存在HDFS上」
既然Hbase是基于HDFS存储的,那它怎么支持随机写呢?我们先往后看,马上就能感受到Hbase的神奇之处!
HBase 的基本概念
在具体讲Hbase之前我们要先理清楚几个概念,否则无法理解到Hbase的精髓,因为它真的太强了!
问题一:何为列式存储?
我们知道hbase是列式存储数据库,首先我们来解释一下什么是列式存储,左边这个json在行式存储中就是一条数据存在一起,而列式存储就是每个列的值存储在一起。
列式存储有什么好处呢?
假设我们现在要统计所有用户年龄总和,行式存储是怎么做的呢?他是不是需要把所有的数据全部取出来进行遍历,磁盘最怕的就是你上来就一堆随机读,瓷针都转冒烟了还没找到你要的数据;当然关系型数据库可以通过添加索引来解决这个问题,但是我们大数据业务动不动就是几十上百个指标,这索引也无法无限加啊
而列式存储是不是只需要取保存年龄的那一列数据取出来就行了?
问题二:何为列族存储?
列式存储我们已经知道了,但是其实Hbase是面向列族存储的,何为列族存储呢?
列族就如这张表,如果不看第一行只看下面三行就和普通的行式存储没什么区别是吧,我们再来看第一行,第一行userInfo,addressInfo,这两个就是两个列族,第二行的就是对应列族下面的列。
Hbase在创建表的时候只需要指定列族,不需要指定列,也就是说,一个列族下可以有随意个列,从表上还可以看出有些数据不存在,比如李四这条数据缺少了城市和区的数据,那这条数据的地址信息的列其实就只有省份这一列
这样做有什么好处呢?
首先,比如我们常见的关系型数据库如mysql,他的某个字段没有值的情况下我们是不是也要用默认值来占着这个空间,而列族存储的话如果没有这个列的值就不需要占额外的空间了,这样可以达到节省空间提高存储效率的效果。
再一个好处就是:
我们开发应该经常遇到这样的场景吧,产品时不时找你说,嘿,兄弟,加个字段呗,紧急需求,麻烦处理下,然后你一看,这个表几千万数据,于是你问他,历史数据怎么办?要不要洗数据?数据量太大,加字段可能要锁表,可能会影响线上业务。产品一听,就加个字段,有这么麻烦?
这时候列族存储是不是就优势非常大了,随便你加,加多少都可以。
事实上,Hbase单表可以存储百亿行数据,同时也可以有数百万个列。
问题三:何为NoSQL?
这里我们说下,Hbase真的非常特殊,它和传统的关系型数据库完全不一样,他是一个NoSQL数据库,那何为NoSQL?
说NoSQL之前我们先回忆一下关系型数据库,如MySQL, oracle,它们可以理解成一个二维表格模型,如execl这样,关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。
Nosql的翻译不是“Not sql”,而是“Not only sql”, 它又被称为非关系型数据库,或者文档数据库,主要用于保存一些结构化或者半结构化的数据,如json,xml之类的。
NoSQL分为几类:
第一类:键值存储,主要存储key-value形式数据,代表有redis,memcached;
第二类:文档存储,主要存储类似json这种一系列的数据项集合,代表有MongoDB;
第三类:列或者列族存储,主要存储日志,画像等,代表有Hbase;
第四类:图存储,主要将数据以图的方式存储,应用于社交网络/推荐系统等,代表有Neo4j等。
HBase 的数据模型
1. 基本概念
我们先来了解一下Hbase一些基本概念。
Table 表:是行和列的组成,但Hbase同一列不只是存一个值,它可以存储这个这个列不同时刻的值,也就是有版本的概念,同时Hbase的多个列可以组成一个列族。
Row Key 行键:既是Hbase表的行健,也是Hbase表的主键,hbase表中的记录是按照rowkey的字典顺序进行存储的,所以RowKey的设计非常重要。
Columns Family 列族:前面说过了,建表需要指定列族,不用指定列,一般是把需要一起查询的数据放在一个列族里,合理的划分列族可以提高查询效率,但是也不能有太多的列族,跨列族访问效率较低。
Column 列:在HBase中用列修饰符(Column Qualifier)来标识每个列。在HBase里边,先有列族,后有列。
2. Hbase 逻辑结构
region是什么?
前面说到HBase是通过Rowkey来排序的,但是我们还是不知道HBase分布式存储的数据分布逻辑是什么?
这里的region就是通过rowkey进行数据切分之后的一个基本单位,数据移动,数据的负载均衡分配都是根据region来的,比如我们一张表被切分成100个region,而现在我们集群有50和存储节点,那么每个节点就会被分到2个region,这样一张表就被均匀的分布到集群的所有节点上。
store是什么?
可以看到store其实是在一个region里面,而且store是一个region下面的一个列族的集合,HBase的一个列族的数据会被存储在一起,它就是store。
3. Hbase 物理结构
说完了hbase的逻辑结构,我们再来看看hbase在磁盘上真正的物理结构。
我们抽取了前面那张表的一个列族数据出来,然后下面这张表这个列族的第一行数据的真正物理结构。
我们先不管timestamp和type这两个字段的话我们可以看到存储rowkey1的用户姓名和用户年龄以及性别分别是存储了三行数据,数据的结构是rowkey+列族+列名,然后就是真正的value数据。
有人会说,这样确实能存,但是数据是不是本来一行存下的现在要膨胀那么多行,是的,hbase就是这样设计的,所以rowkey的设计就非常字段了,因为他冗余了很多,所以不能过长,但又需要唯一。
这里我们回过头来看看Timstamp和Type是什么?
- Timestamp:其实就是时间戳,他标示着这条数据的版本,每次新增数据都会默认保存写入数据时候的时间戳,这样就能区分哪条是最新的数据
- Type:是标示这条数据的类型,put指新增,delete表示删除
之前我们是不是说过hdfs是不支持修改和删除的吗?然后hbase又是基于hdfs来实现的吗?那它为什么就能实现修改和删除呢?有这种好事,hdfs自己为啥不干呢?
而Timestamp和Type这两个字段就是它实现修改和删除的关键所在!
首先我们讲讲怎么实现修改的?
修改其实好理解,既然hdfs不能修改,那我hbase就不修改了,我再加一条数据不就行了,我每次都取最新的那条,其实不就是相当于我修改了这条数据了吗,是不是好狗啊
再来看看删除是如何实现的?
删除其实一样的,既然你不让删,那我还是新增一条数据,只是新增的这条数据的类型是delete,这样我在查询的时候拿到最新的这条数据,发现它的类型是delete,那我就知道这条数据已经被删除了。
这就是hbase支持随机修改和删除的主要技术细节,不过还有很多这里没介绍,比如版本数据的清楚啊,删除数据的清除逻辑之类的,不可能一直保留下去的。
4. Hbase 的Key-Value
我们再单独提出一条数据,可以看到要拿到value的话,那么我们必须要知道rowkey,列族,列,时间戳,数据类型,
我们把这些字段拼接成一个字段,是不是就像是key-value结构了。所以hbase也可以说是面向key-value存储的。
HBase 的架构
Client:客户端是整个hbase系统的入口,可以通过客户端直接操作hbase,客户端通过RPC协议和hmaster以及hregionserver通信。
hbase也是主从架构。
Hmaster:是主节点,并且一个hbase集群是会启动多个hmaster节点,通过zookeeper选举机制来保障可用性.
Hmaster主要职责就是:
1、负责管理用户对表的增删改查操作;
2、管理hregionserver的负载均衡,调整hregion的分布;
3、hregionserver如果挂了,则对hregionserver上的hregion进行迁移。
Hregionserver:主要负责响应用户的I/O请求,hregionserver节点内部管理了一系统hregion对象,也就是一张表被切割后的一部分
Zookeeper:在这里不止是对hmaster的高可用作用,他还有个作用是保存hbase表的元数据,就是这张表对应存储在哪些hregionserver上,这块按理是hmaster来保存的,但是要访问hmaster一定还是要经过zookeeper,所以直接放在zk上会性能更好。
细看HRegionServer 内部
前面说到我们知道一张表会被切分成若干个region,然后这些region会被均匀的放入不同和节点也就是HRegionServer进行管理。
Hregion其实就是保存若干个Store,一个列族的数据是存储在一起的,所以Store是存储了一个列族的数据。
Store里边有啥?看上图,有Mem Store、Store File、HFile,我们再来看看里边都代表啥含义。
HBase在写数据的时候,会先写到Mem Store也就是内存中,当MemStore超过一定阈值,就会将内存中的数据刷写到硬盘上,形成StoreFile,而StoreFile底层是以HFile的格式保存,HFile是HBase中KeyValue数据的存储格式。它实际上是存储在HDFS上
所以说:Mem Store我们可以理解为内存 buffer,HFile是HBase实际存储的数据格式,而StoreFile只是HBase里的一个名字。
回到HRegionServer上,我们还漏了一块,就是HLog。
这里其实特别好理解了,我们写数据的时候是先写到内存的,为了防止机器宕机,内存的数据没刷到磁盘中就挂了。我们在写Mem store的时候还会写一份HLog。
这个HLog是顺序写到磁盘的,所以速度还是挺快的(是不是有似曾相似的感觉)。
HBase 的总结
- HBase是一个NoSQL数据库,一般我们用它来存储海量的数据(因为它基于HDFS分布式文件系统上构建的)。
- HBase的一行记录由一个RowKey和一个或多个的列以及它的值所组成。先有列族后有列,列可以随意添加。
- HBase的增删改记录都有「版本」,默认以时间戳的方式实现。
- HBase的读写都经过Zookeeper去拉取meta数据,然后找到HRegionServer。
附录:HBase Shell 操作
- 插入数据到表
put 'student','1001','info:sex','male'
put 'student','1001','info:age','18'
put 'student','1002','info:name','janna'
put 'student','1002','info:sex','female'
put 'student','1002','info:age','20' - Scan方式查看数据
- 扫描全表
scan 'student'
- 限定开始位置扫描全表
scan 'student',{STARTROW=>'1001'}
- 限定开始和结束为止扫描全表
scan 'student',{STARTROW=>'1001',STOPROW=>'1001'}
- 扫描全表数据(包含被标记删除或者应该被覆盖的数据)
scan 'student',{RAW=>true,VERSIONS=>10}
- 扫描全表
- Get方式查看数据(必须指定RowKey)
- 指定RowKey查询
get 'student','1001'
- 指定RowKey+列族查询
get 'student','1001','info'
- 指定RowKey+列族+列名查询
get 'student','1001','info:name'
- 指定获取数据版本查询
get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}
- 指定RowKey查询
- 统计表数据行数
count 'student'
- 更新数据(和插入一致)
put 'student','1001','info:name','Nick'
- 删除数据
- 指定RowKey+列族+列名删除
delete 'student','1001','info:name'
- 指定RowKey+列族删除
delete 'student','1001','info'
- 指定RowKey删除
delete 'student','1001'
- 清空表数据
truncate 'student'
- 指定RowKey+列族+列名删除