hdfs
- hdfs组件
- Namenode : 存储系统元数据、 namespace、管理datanode、接受datanode状态汇报
- Datanode: 存储块数据,响应客户端的块的读写,接收namenode的块管理理指令
- Block: HDFS存储数据的基本单位,默认值是128MB,实际块大小0~128MB
- Rack: 机架,对datanode所在主机的物理标识,标识主机的位置,优化存储和计算
- 为什么HDFS不适合小文件存储?
情况 | Namenode占用 | Datanode占用 |
10000个文件总共128MB | 10000个元数据 | 128MN |
1个128MB文件 | 1个元数据 | 128MB |
- 小文件过多,会过多占用namenode的内存,并浪费block
- HDFS适用于高吞吐量,而不适合低时间延迟的访问。文件过小,寻道时间大于数据读写时间,这不符合HDFS的设计原则
- Namenode和SecondaryNameNode区别
Namenode主要维护两个组件,一个是 fsimage ,一个是 editlog
- fsimage保存了最新的元数据检查点,包含了整个HDFS文件系统的所有目录和文件的信息。对于文件来说包括了数据块描述信息、修改时间、访问时间等;对于目录来说包括修改时间、访问权限控制信息(目录所属用户,所在组)等。
- editlog主要是在NameNode已经启动情况下对HDFS进⾏的各种更新操作进行记录,HDFS客户端执行所有的写操作都会被记录到editlog中。
- hdfs 写流程
- 客户端跟namenode通信请求上传文件,namenode检查目标文件是否已存在,父目录是否存在
- namenode返回是否可以上传
- client请求第一个 block该传输到哪些datanode服务器上
- namenode返回3个datanode服务器ABC
- client请求3台dn中的一台A上传数据(本质上是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将真个pipeline建立完成,逐级返回客户端
- client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,A收到一个packet就会传给B,B传给C;A每传一个packet会放入一个应答队列等待应答
- 当一个block传输完成之后,client再次请求namenode上传第二个block的服务器
- hdfs 读流程
- client跟namenode通信查询元数据,找到文件块所在的datanode服务器
- 挑选一台datanode(就近原则,然后随机)服务器,请求建立socket流
- datanode开始发送数据(从磁盘里面读取数据放入流,以packet为单位来做校验)
- 客户端以packet为单位接收,现在本地缓存,然后写入目标文件
MapReduce
MapReduce的思想就是“分而治之”或者“化繁为简”。
-
Mapper
负责“分”,即把复杂的任务分解为若干个“简单的任务”来处理。 “简单的任务”包含三层含义:
- 是数据或计算的规模相对原任务要大大缩小;
- 是就近计算原则,即任务会分配到存放着所需数据的节点上进行计算;
- 是这些小任务可以并行计算,彼此间几乎没有依赖关系。
-
Reducer
主要负责对map阶段的结果进行汇总
yarn
-
yarn的流程
- 提交应用给yarn集群的ResourceManager
- 某一个空闲的NodeManager启动一个Java进程App Master
- App Master根据任务的计算规模带线啊哦请求ResourceManager分配计算资源
- ResourceManager会在管理的NodeManager中启动相应单位的计算容器
- 在计算容器中运行application的计算任务
- 在计算任务期间,计算容器汇报计算进度告诉给App Master
- 执行完成后,AppMaster告知ResourceManager任务计算完成
- 释放资源
-
yarn的相关任务组件
-
ResourceManager:是在系统中的所有应用程序之间仲裁资源的最终权限。
-
NodeManager:是每台机器框架代理,负责容器,监视其资源使用情况(CPU,内存,磁盘,网络)并将其报告给ResourceManager的Scheduler
-
App Master :应用的Master负责任务计算过程中的任务监控、故障转移,每个Job只有一个。
-
Container:表示一个计算进程
-
zookeeper
数据树指的在ZooKeeper维护了一个类似于Linux文件系统树形的层次空间。
- ZooKeeper的数据树中一个节点,都称为ZNode
- ZooKeeper的一个ZNode,即使路径标示又可以存放一个简单的数据(1MB)
- ZooKeeper维护一个树形的层次化空间,数据树
ZNode的类型
- 永久节点(Persistent Node ): 当ZooKeeper客户端在ZooKeeper服务器写入(创建)一个永久节点,永久存在,只有当客户端在调用删除指令时,永久节点销毁。
- 临时节点(Ephemeral Node ): 临时节点的生命周期依赖与创建它的会话(session),一旦会话结束,临时节点自动销毁。注意:临时节点下不允许有子节点
- 顺序节点(Sequential Node ): 不能单独使用,需要搭配永久或者临时节点使用。创建一个顺序节点时,ZooKeeper自动的节点名后面加上一个10位的有序的序号(原子递增)。如:创建一个永久顺序节点,节点名叫
/app1
,会自动添加有序的序号/app10000000000
Watches
Watches是ZooKeeper中非常之重要的一个组件,称为监视器。
Watches可以监视ZNode的改变:
- 监视指定的ZNode上的数据的改变
- 监视指定的ZNode子节点数量的改变
zookeeper的应用
-
统一命名服务 Dubbo使用Zookeeper作为注册中心,临时节点
-
分布式配置中心
-
集群选举
-
分布式锁
- 数据库实现
竞争锁:插入唯一记录
释放锁:删除唯一记录
优点:实现简单
缺点:效率低,单点故障,容易出现死锁
2. Redis实现
竞争锁:string类型的 setnx k v
释放锁:string类型的 del k v
优点:效率高,不会出现单点故障,expire time 设定不会发生死锁
缺点:没有有效的队列机制
3. zookeeper实现 临时顺序节点 + 子节点数量改变监视器
竞争锁:临时顺序节点序号最小的获得锁
释放锁:会话结束 锁释放
优点:排队等待机制 不容易死锁 不容易出现单点故障 高效
HBASE
列存储(Column-Store)与行存储(Row-Store)的区别
- 存储格式优势
分析类查询往往只查询一个表里面很少的几个字段,Column-Store只需要从磁盘读取用户查询的Column,而Row-Store读取每一条记录的时候你会把所有Column的数据读出来,在IO上Column-Store比Row-Store效率高很多,因此性能更好。 - 查询引擎层次的优化
块遍历(Block Iteration)、压缩(Compression)、
延迟物化(Late Materialization)、不可见链接(Invisible Join)
块遍历 对于数据的批量化操作
压缩 对数据进行更高效的编码, 使得我们可以以更少的空间表达相同的意思
延迟物化
- 物化:为了能够把底层存储格式(面向Column的), 跟用户查询表达的意思(Row)对应上,在一个查询的生命周期的某个时间点,一定要把数据转换成Row的形式
- 延迟物化:这个物化的时机尽量的拖延到整个查询生命周期的后期
- 例如:查询的时候先执行过滤条件,然后再在过滤后的数据中加上我们需要的字段
- 好处:
- 关系代数里面的 selection 和 aggregation 都会产生一些不必要的物化操作,从一种形式的tuple, 变成另外一种形式的tuple。如果对物化进行延迟的话,可以减少物化的开销(因为要物化的字段少了),甚至直接不需要物化了。‘
- 如果Column数据是以面向Column的压缩方式进行压缩的话,如果要进行物化那么就必须先解压,而这就使得我们之前提到的可以直接在压缩数据上进行查询的优势荡然无存了。
- 操作系统Cache的利用率会更好一点,因为不会被同一个Row里面其它无关的属性污染Cache Line。
- 块遍历 的优化手段对Column类型的数据效果更好,因为数据以Column形式保存在一起,数据是定长的可能性更大,而如果Row形式保存在一起数据是定长的可能性非常小(因为你一行数据里面只要有一个是非定长的,比如VARCHAR,那么整行数据都是非定长的)。
Invisible Join:
- 实现思路:
把所有过滤条件应用到每个维度表上,得到符合条件的维度表的主键(同时也是事实表的外键)。
遍历事实表,并且查询第一步得到的所有外键的值,得到符合条件的bitmap(s), 这里会有多个 bitmap,因为维度表可能有多个。
对第二步的多个bitmap做AND操作,得到最终事实表里面符合过滤条件的bitmap。
根据第三步的事实表的bitmap以及第一步的符合条件的维度表的主键值,组装出最终的返回值。
- 主键:rowkey 获取数据的唯一标示,不能重复,根据字典顺序自动排序,底层存储时byte[]
- 列簇:column family 多个列的集合,通常一个列簇中存放的是一组功能相似或者业务相近的列的集合。
- 单元格:rowkey + cf + column定位一个Cell,Cell允许有多个数据版本,默认为1个
- 多版本:Cell允许有多个数据版本
- 版本号:系统当前的时间戳,默认会将时间戳最新的Cell数据返回给用户
- 列:Column 列簇中一个字段,用来存放某一类别的数据
特点
- 大:一个表可以有上百亿行,上百万列
- 面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。
- 结构稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
- 无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可
以有截然不同的列。 - 数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时
的时间戳。 - 数据类型单一:HBase中的数据在底层存储时都是
byte[]
,可以存放任意类型的数据
Kafka
Kafka具备三项关键能力:
- 发布订阅记录流(Record),类似于消息队列(MQ)或者企业级消息系统
- 存储记录流,以一种容错持久化方式
- 实时处理加工流数据
Kafka的应用场景:
- 构建实时的流数据管道,可靠的在系统和应用间获取数据(MQ)
- 构建实时的流数据应用,传输或者处理加工流数据
Kafka中的核心概念:
- Broker(中间人或者代理人): 一个kafka服务实例
- Topic(主题): 某一个类别的记录
- Partition(分区): 对主题进行数据分区,一个主题有多个数据分区构成
- Replication(复制): 主题主分区中的数据备份,故障恢复
- Leader(主分区): 主题的主分区 读写操作默认使用主分区
- Follower(复制分区): 同步主分区中的数据(冗余备份),并且当主分区不可用时,某个follower会自动升级为主分区
- Offset(偏移量): 标识读写操作的位置,读offset <= 写offset
Kafka的四大核心API:
- The Producer API:允许应用发布记录流给一个或者多个Topic
- The Consumer API : 允许应用订阅一个或者多个主题,并且对主题中新产生的记录进行处理
- The Streams API : 允许应用扮演流处理器,消费来自于一个或者多个主题流数据记录,并且将处理的结果输出到一个或者多个主题中,Streams API可以高效的处理传输数据。
- The Connector API: 连接外部的存储系统
工作原理
- 首先
Producer
产生Record
发送给指定的Kafka Topic(Topic实质是有多个分区构成,每一个分区都会相应的复制分区),在真正存放到Kafka集群时会进行计算key.hashCode%topicPartitionNums等于要存放的分区序号。 Leader
分区中的数据会自动同步到Follower
分区中,ZooKeeper
会实时监控服务健康信息,一旦发生故障,会立即进行故障转移操作(将一个Follower复制分区自动升级为Leader主分区)- Kafka一个分区实际上是一个有序的Record的
Queue
(符合队列的数据结构,先进先出), 分区中新增的数据,会添加到队列的末尾,在处理时,会从队列的头部开始消费数据。Queue
在标识读写操作位置时,会使用一个offset
(读的offset <= 写的offset) - 最后
Consumer
会订阅一个Kafka Topic,一旦Topic中有新的数据产生,Conumser立即拉取最新的记录,进行相应的业务处理。
spark
Apache Spark
是一个快如闪电的统一的分析引擎(仅仅是一款分析引擎,不提供存储服务)
快:相比较于第一代基于磁盘计算的离线分析框架
MapReduce而言,Spark基于内存计算
较快
统一
:Spark提供统一的API访问接口,实现了批处理
和流处理
的统一,并且提供ETL
功能
同时提供对大规模数据集的全栈式解决方案:批处理
、流处理
、SQL
、Machine Learning
、图形关系分析
等
计算速度快的原因
-
使用先进的
DAG(有向无环图)
设计MapReduce
:矢量计算 起点(Map
并行度)—> 终点(Reduce
并行度)Spark
:有向无环图 起点 —>第一阶段(并行度)—>第二阶段(并行度)—>第n阶段(并行度)—>终点 -
和
MapReduce
不同,MapReduce
运算产生的中间结果(溢写文件)会存储到磁盘,然后再启动下一轮的计算,计算的性能在很大程度上受限于磁盘的读写操作,而Spark
的计算是将任务拆分成若干个Stage
,每个阶段的计算结果都可以缓存在内存中,在进行迭代计算时,计算结果可以复用,因此极大降低了重复计算和磁盘读写所消耗的时间 -
Spark SQL
可利用Query Optimizer
优化用户SQL
**Spark计算架构 **
Application
:等价于MapReduce
中的Job
任务,包含Dirver
和Executor
进程
Driver
:用户主程序,用于创建SparkContext
,协调整个计算任务(等价于MRAppMaster
)
ClusterManager
:主要负责集群的计算资源的管理,并不负责任务调度(等价于ResourceManager
)
WorkerNode
:泛指能够运行Apllication
代码的集群中的机器
Executor
:每个Application
任务都有自己的进程集合,这些进程负责运行Task
(线程),并且存储计算的中间结果
Task
:每个计算任务,会被划分为若干个Stage阶段
,每个阶段都有自己的并行度,并行度表示线程数目(Task数目)
Stage
:每个任务会被划分为若干个阶段,每个阶段都有自己的并行度,阶段与阶段之间有相互的依赖关系(RDD血统)
-
用户main函数(Driver)初始化SparkContext,并且向ClusterManager(负责资源管理)申请计算资源和所需进程
-
Worker Nod汇报自身计算状态,接受ClusterManager命令,启动Executor(JVM进程)
-
申请资源,会反向注册到Driver中
-
DAGScheduler实现计算任务阶段的划分(stage )
note:spark的shuffle是在DAGSchedular划分Stage的时候产生的,TaskSchedule要分发Stage到各个 worker的executor
-
将阶段任务映射为TaskSet,提交任务TaskSet
spark作业执行流程
- 客户端提交作业
- Driver启动流程
- Driver申请资源并启动其余Executor(即Container)
- Executor启动流程
- 作业调度,生成stages与tasks。
- Task调度到Executor上,Executor启动线程执行Task逻辑
- Driver管理Task状态
- Task完成,Stage完成,作业完成
**Qustion:**RDD(弹性分布式数据集Resilient Distributed Dataset)、DataSet、DataFrame区别与相互转换
- RDD五大特性
RDD是由一系列的分区组成。
操作一个RDD实际上操作的是RDD的所有分区。
RDD之间存在各种依赖关系。
可选的特性,key-value型的RDD是通过hash进行分区。
RDD的每一个分区在计算时会选择最佳的计算位置
RDD是弹性数据集,“弹性”体现在哪里呢?你觉得RDD有哪些缺陷?
自动进行内存和磁盘切换
基于lineage的高效容错
task如果失败会特定次数的重试
stage如果失败会自动进行特定次数的重试,而且只会只计算失败的分片
checkpoint【每次对RDD操作都会产生新的RDD,如果链条比较长,计算比较笨重,就把数据放在硬盘中】和persist 【内存或磁盘中对数据进行复用】(检查点、持久化)
数据调度弹性:DAG TASK 和资源管理无关
数据分片的高度弹性repartion
缺陷:
惰性计算的缺陷也是明显的:中间数据默认不会保存,每次动作操作都会对数据重复计算,某些计算量比较大的操作可能会影响到系统的运算效率
DataFrame是一种以RDD为基础的分布式数据集
RDD和DataFrame的区别
DataFrame与RDD的主要区别在于,DataFrame带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。
RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。 DataFrame底层是以RDD为基础的分布式数据集,和RDD的主要区别的是:RDD中没有schema信息,而DataFrame中数据每一行都包含schema
Dataset是一个由特定领域的对象组成强类型(typedrel)集合,可以使用函数(DSL)或关系运算(SQL)进行并行的转换操作。 每个Dataset 还有一个称为“DataFrame”的无类型(untypedrel)视图,它是[[Row]]的数据集。
RDD和Dataset的区别
Dataset与RDD类似,但是,它们不使用Java序列化或Kryo,而是使用专用的Encoder编码器来序列化对象以便通过网络进行处理或传输。虽然Encoder编码器和标准序列化都负责将对象转换为字节,但Encoder编码器是动态生成的代码,并使用一种格式,允许Spark执行许多操作,如过滤,排序和散列,而无需将字节反序列化为对象
Dataset和DataFrame的区别与联系
区别:
- Dataset是强类型typedrel的,会在编译的时候进行类型检测;而DataFrame是弱类型untypedrel的,在执行的时候进行类型检测;
- Dataset是通过Encoder进行序列化,支持动态的生成代码,直接在bytes的层面进行排序,过滤等的操作;而DataFrame是采用可选的java的标准序列化或是kyro进行序列化
联系:
- 在spark2.x,DataFrame和Dataset的api进行了统一
- 在语法角度,DataFrame是Dataset中每一个元素为Row类型的特殊情况
type DataFrame = Dataset[Row]- DataFrame和Dataset实质上都是一个逻辑计划,并且是懒加载的,都包含着scahema信息,只有到数据要读取的时候,才会将逻辑计划进行分析和优化,并最终转化为RDD
- 二者由于api是统一的,所以都可以采用DSL和SQL方式进行开发,都可以通过sparksession对象进行创建或者是通过transform转化操作得到
RDD转DataFrame原因及方式
可以将RDD转成DataFrame之后,借用sparksql和sql以及HQL语句快速方便的使用sql语句统计和查询,比如说分组排名(row_number() over()) 分析函数和窗口函数去实现占比分析。
将RDD转化为DataFrame有两种方式:
方式一:通过反射推断schema 要求:RDD的元素类型必须是case class
方式二、编程指定schema 要求:RDD的元素类型必须是Row 自己编写schema(StructType) 调用SparkSession的createDatafrmame(RDD[Row],schema)
DataFrame转RDD原因及方式
解决一些使用sql难以处理的统计分析
将数据写入Mysql
a.DataFrame的write.jdbc,仅支持四种模式:append、overwrite、ignore、default
b.使用rdd的话,除了上述以外还支持insert 和 update操作,还支持数据库连接池 (自定 义,第三方:c3p0 hibernate mybatis)方式,批量高效将大量数据写入 Mysql
方式: DataFrame转换为RDD相对来说比较简单,只需要调用DataFrame的RDD算子(toDF)即可。