Hadoop生态圈中各组件工作流程分析
- HDFS YARN MapReduce三者关系
- HDFS写数据流程
- HDFS读数据流程
- 切片源码
- InputFormat
- MapReduce工作流程
- shuffle机制
- Yarn工作机制
- Map Task工作机制
- Reduce Task 工作机制
- MapReduce工作机制
- MapReduce优化性能
- ZooKeeper
- HA
- Hive
- Hive的企业级调优(面试重点)
- Flume
- Kafka
- maxwell
- datax
- Spark
- 数据仓库data warehouse
HDFS YARN MapReduce三者关系
关系图
文字描述
- 客户端提交文件给Hadoop -> Yarn -> ResourceManager
- ResourceManager启动App Master
- App Master 申请资源给Container
- 在Container中跑MapTask(数据分成了几片就有几个Map Task)
- Map Task 中的数据从DataNode中来
- Map Task 跑完把数据结果传给Reducer Task
- Reducer Task跑完把结果存入HDFS
HDFS写数据流程
流程图
过程描述
- 客户端创建一个 分布式文件系统对象 通过它向NameNode请求上传文件D:\io\input\a.txt
- NameNode检查目录树是否可以上传(权限、目录是否已经存在等),响应可以上传
- 客户端向NameNode请求上传Block(0-128M)
- NameNode返回dn1 dn2 dn3节点 (副本节点选择:本地节点 其他机架一个节点 其他机架另一个节点 选距离最近的 拓扑)
- 客户端和DataNode1建立传输通道,然后DataNode1和DataNode2建立传输通道,DataNode2和DataNode3建立传输通道,应答成功就可以开始传输数据了
- 从本地读取数据后开始传输,一个包一个包的传。Packet 64k 含 chunk512byte + chunksum4byte
- DataNode节点会一个一个的把Packet写在磁盘上(落盘)
- 传输完成后若还有块er没传完,回到第三步直到所以块都传输完成
HDFS读数据流程
流程图
过程描述
- 客户端创建文件系统对象 Distributed FileSystem 向NameNode发送读数据请求
- NameNode看客户端有没有读权限并看看自己有没有该数据,返回目标文件的 元数据
- 客户端创建流 读数据流
- 向目标DataNode节点发送读数据请求(块信息等)
- 目标节点向客户端传输数据
- 传输完成若还有块等待读取数据就重复步骤4直到数据都读完
- 将文件改名后关闭流
切片源码
/*
getFormatMinSplitSize() : 返回1
getMinSplitSize(job) :如果配置了mapreduce.input.fileinputformat.split.minsize参数返回参数设置的值
否则返回默认值1
*/
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
/*
如果设置了mapreduce.input.fileinputformat.split.maxsize参数那么返回该参数设置的值
否则返回Long.MAX_VALUE
*/
long maxSize = getMaxSplitSize(job);
//该集合用来存放切片信息(一个切片就是一个对象)
//放入到该集合的对象是FileSplit 但是泛型是InputSplit 说明FileSplit和InputSplit有继承关系
//FileSplit是InputSplit的子类
List<InputSplit> splits = new ArrayList<InputSplit>();
//获取输入目录中所有的文件或目录状态
List<FileStatus> files = listStatus(job);
//获取文件的路径
Path path = file.getPath();
//获取文件的大小
long length = file.getLen();
//块大小
long blockSize = file.getBlockSize();
/*
切片大小
默认 : 片大小 = 块大小
需求:
片大小 > 块大小 :修改minSize大小
片大小 < 块大小 :修改maxSize大小
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
*/
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
//剩余文件大小
long bytesRemaining = length;
/*
开始切片 :
((double) bytesRemaining)/splitSize > SPLIT_SLOP : 剩余文件大小 / 切片大小 > 1.1 (为了防止最后1片太小)
*/
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
/*
makeSplit : 创建FileSplit对象
参数length-bytesRemaining :片的起始位置
参数splitSize :切片大小
将FileSplit对象放入到splits集合中
*/
splits.add(makeSplit(path, length-bytesRemaining, splitSize,
blkLocations[blkIndex].getHosts(),
blkLocations[blkIndex].getCachedHosts()));
//重新计算剩余文件大小 :将片大小从剩余文件大小减掉
bytesRemaining -= splitSize;
}
//将剩余文件大小整体切一片
if (bytesRemaining != 0) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkIndex].getHosts(),
blkLocations[blkIndex].getCachedHosts()));
}
not splitable
//如果文件不可切整个文件是1片
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
blkLocations[0].getCachedHosts()));
InputFormat
一 InputFormat : 抽象类
作用 :①切片 ②读取数据
//切片
public abstract
List<InputSplit> getSplits(JobContext context
) throws IOException, InterruptedException;
//创建RecordReader对象 该对象用来读取数据
public abstract
RecordReader<K,V> createRecordReader(InputSplit split,
TaskAttemptContext context
) throws IOException,
InterruptedException;
二 InputFormat的继承树
|-----InputFormat 抽象类
|------FileInputFormat 抽象类
|-------TextInputFormat 默认使用的InputFormat的类
三 FileInputFormat 抽象类
1.FileInputFormat是抽象类 继承了 InputFormat
2.FileInputFormat实现了父类的getSplits方法
四 TextInputFormat 默认使用的InputFormat的类
1.TextInputFormat是默认使用的InputFormat的类 继承了FileInputFormat
2.TextInputFormat实现了createRecordReader方法
3.createRecordReader方法返回了LineRecordReader,LineRecordReader是RecordReader的子类
4.LineRecordReader是真正用来读取文件中的数据的那个对象的所属类
public RecordReader<LongWritable, Text>
createRecordReader(InputSplit split,
TaskAttemptContext context) {
return new LineRecordReader(recordDelimiterBytes);
}
conbineTextInputFormat切片机制
将大量的小文件合并成一个大的Map Task的过程
虚拟存储过程 切片过程
MapReduce工作流程
概念图
过程描述
- 准备待处理数据a.txt 200M 块大小是128M 切两片(若想改变切片大小就设置conf参数,具体修改见切片源码)
- InputFormat -> FileInputFormat -> TextInputFormat -> getSplit 对数据进行切片处理,上传jar包,提交job信息给YarnRunner 创建App Master
- App Master 申请资源,生成两个Map Task
- Map Task 用 InputFormat -> RecordReader -> LineRecondReader 读数据
- 然后使用Mapper的map方法进行数据处理
- 接下来就是shuffle过程,在下一章节详细讲解
shuffle机制
shuffle流程图解
文字描述
- map方法写出的数据流向了缓冲区
- 该缓冲区在数据量达到80%时会进行排序,排序完成的数据会形成多个分区直接写入磁盘,每轮数据都会按该分区算法进行分区,方便下一步归并
- 所有数据都处理完成后就会进行归并排序,相同分区的数据进行归并
- 归并完成后按分区进行combiner合并压缩即:将1万条aaa 1 -> aaa 10000
- 将处理结果写入磁盘
- 进行过上述操作后将所有 Map Task 的相同分区放在一起进行的归并排序
- 最后数据进入 reduce 方法
Yarn工作机制
流程图
调度算法
先进先出调度器(FIFO)
容量调度器(Capacity Scheduler)
公平调度器(Fair Scheduler)
Map Task工作机制
流程图
Reduce Task 工作机制
流程图
MapReduce工作机制
流程图
MapReduce优化性能
map优化
Reduce优化
数据倾斜问题
Hadoop小文件解决方案
- Hadoop Archive(HAR归档)
是一个高效的将小文件放入HDFS块中的文件存档工具,能够将多个小文件打包成一个HAR文件,从而达到减少NameNode的内存使用。 - CombineTextInputFormat
CombineTextInputFormat用于将多个小文件在切片过程中生成一个单独的切片或者少量的切片。
ZooKeeper
ZooKeeper工作机制
ZooKeeper特点
Leader选举机制
zkCli.sh进入zk客户端
HA
HDFS-HA核心问题
- 怎么保证三台namenode的数据一致
- Fsimage:让一台nn生成数据,让其他机器nn同步。
- Edits:需要引进新的模块JournalNode来保证edtis的文件的数据一致性。
- 怎么让同时只有一台nn是active,其他所有是standby的
Zookeeper居中协调,选举active - 2nn在ha架构中并不存在,定期合并fsimage和edtis的活谁来干
由standby的nn来干。 - 如果nn真的发生了问题,怎么让其他的nn上位干活
自动故障转移
自动故障转移工作机制
Yarn-HA工作机制
Hadoop-HA集群最终规划
hadoop102 | hadoop103 | hadoop104 |
---|---|---|
NameNode | NameNode | NameNode |
JournalNode | JournalNode | JournalNode |
DataNode | DataNode | DataNode |
Zookeeper | Zookeeper | Zookeeper |
ZKFC | ZKFC | ZKFC |
ResourceManager | ResourceManager | ResourceManager |
NodeManager | NodeManager | NodeManager |
- NameNode:HDFS中存储元数据信息的服务
- JournalNode:保证edtis的文件的数据一致性的服务
- DataNode:HDFS中真正存储数据的节点服务
- Zookeeper:文件系统+通知机制的服务
- ZKFC:ZooKeeper Failover Controller,监视和管理 Hadoop 的 NameNode 服务的高可用性
- ResourceManager:Yarn中处理客户端请求、监控NodeManager、启动或监控ApplicationMaster、资源的分配与调度的服务
- NodeManager:Yarn中管理单个节点上的资源、处理来自ResourceManager的命令、处理来自ApplicationMaster的命令的服务
Hive
基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。
Hive架构原理
抽象语法树
逻辑计划与物理计划
hiveserver2
Hive的hiveserver2服务的作用是提供jdbc/odbc接口,为用户提供远程访问Hive数据的功能,例如用户期望在个人电脑中访问远程服务中的Hive数据,就需要用到Hiveserver2。
Hive的metastore服务的作用是为Hive CLI或者Hiveserver2提供元数据访问接口。
语法
基础语法和MySQL相同
炸裂函数
案例演示:
数据准备
movie | category |
---|---|
《疑犯追踪》 | 悬疑,动作,科幻,剧情 |
《Lie to me》 | 悬疑,警匪,动作,心理,剧情 |
《战狼2》 | 战争,动作,灾难 |
create table movie_info(
movie string, --电影名称
category string --电影分类
)
row format delimited fields terminated by "\t";
insert overwrite table movie_info
values ("《疑犯追踪》", "悬疑,动作,科幻,剧情"),
("《Lie to me》", "悬疑,警匪,动作,心理,剧情"),
("《战狼2》", "战争,动作,灾难");
需求说明:根据上述电影信息表,统计各分类的电影数量,期望结果如下:
类型 | 数量 |
---|---|
剧情 | 2 |
动作 | 3 |
心理 | 1 |
悬疑 | 2 |
战争 | 1 |
灾难 | 1 |
科幻 | 1 |
警匪 | 1 |
select
cate,
count(*)
from
(
select
movie,
cate
from
(
select
movie,
split(category,',') cates
from movie_info
)t1 lateral view explode(cates) tmp as cate
)t2
group by cate;
窗口函数(开窗函数)
按照功能,常用窗口可划分为如下几类:聚合函数、跨行取值函数、排名函数。
1)聚合函数
max:最大值。
min:最小值。
sum:求和。
avg:平均值。
count:计数。
2)跨行取值函数
(1)lead和lag
(2)first_value last_value
(3)排名函数
自定义函数
1)Hive自带了一些函数,比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。
2)当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。
3)根据用户自定义函数类别分为以下三种:
(1)UDF(User-Defined-Function)
一进一出。
(2)UDAF(User-Defined Aggregation Function)
用户自定义聚合函数,多进一出。
类似于:count/max/min
(3)UDTF(User-Defined Table-Generating Functions)
用户自定义表生成函数,一进多出。
如lateral view explode()
4)官方文档地址
https://cwiki.apache.org/confluence/display/Hive/HivePlugins
分区表(分目录)和分桶表(分文件)
create table dept_partition(
deptno int, --部门编号
dname string, --部门名称
loc string --部门位置
)
partitioned by (day string)
row format delimited fields terminated by '\t';
load data local inpath '/opt/module/hive/datas/dept_20220401.log' into table dept_partition partition(day='20220401');
按day分区,day是一个新列,里面可以是空的导数据的时候再写20220401,但创建表时必须有day这个字段类型也不能错!是string
insert
insert overwrite table dept_partition partition (day = '20220402')
select deptno, dname, loc
from dept_partition
where day = '2020-04-01';
分区表基本操作
(1)查看所有分区信息
hive> show partitions dept_partition;
(2)增加分区
①创建单个分区
hive (default)>
alter table dept_partition
add partition(day=‘20220403’);
②同时创建多个分区(分区之间不能有逗号)
hive (default)>
alter table dept_partition
add partition(day=‘20220404’) partition(day=‘20220405’);
(3)删除分区
①删除单个分区
hive (default)>
alter table dept_partition
drop partition (day=‘20220403’);
②同时删除多个分区(分区之间必须有逗号)
hive (default)>
alter table dept_partition
drop partition (day=‘20220404’), partition(day=‘20220405’);
二级分区表
思考:如果一天内的日志数据量也很大,如何再将数据拆分?答案是二级分区表,例如可以在按天分区的基础上,再对每天的数据按小时进行分区。
1)二级分区表建表语句
hive (default)>
create table dept_partition2(
deptno int, – 部门编号
dname string, – 部门名称
loc string – 部门位置
)
partitioned by (day string, hour string)
row format delimited fields terminated by ‘\t’;
2)数据装载语句
hive (default)>
load data local inpath ‘/opt/module/hive/datas/dept_20220401.log’
into table dept_partition2
partition(day=‘20220401’, hour=‘12’);
3)查询分区数据
hive (default)>
select
*
from dept_partition2
where day=‘20220401’ and hour=‘12’;
动态分区
动态分区是指向分区表insert数据时,被写往的分区不由用户指定,而是由每行数据的最后一个字段的值来动态的决定。使用动态分区,可只用一个insert语句将数据写入多个分区。
1)动态分区相关参数
(1)动态分区功能总开关(默认true,开启)
set hive.exec.dynamic.partition=true
(2)严格模式和非严格模式
动态分区的模式,默认strict(严格模式),要求必须指定至少一个分区为静态分区,nonstrict(非严格模式)允许所有的分区字段都使用动态分区。
set hive.exec.dynamic.partition.mode=nonstrict
(3)一条insert语句可同时创建的最大的分区个数,默认为1000。
set hive.exec.max.dynamic.partitions=1000
(4)单个Mapper或者Reducer可同时创建的最大的分区个数,默认为100。
set hive.exec.max.dynamic.partitions.pernode=100
(5)一条insert语句可以创建的最大的文件个数,默认100000。
hive.exec.max.created.files=100000
(6)当查询结果为空时且进行动态分区时,是否抛出异常,默认false。
hive.error.on.empty.partition=false
分桶表基本语法
1)建表语句
hive (default)>
create table stu_buck(
id int,
name string
)
clustered by(id)
into 4 buckets
row format delimited fields terminated by ‘\t’;
2)数据装载
(1)数据准备
在/opt/module/hive/datas/路径上创建student.txt文件,并输入如下内容。
1001 student1
1002 student2
1003 student3
1004 student4
1005 student5
1006 student6
1007 student7
1008 student8
1009 student9
1010 student10
1011 student11
1012 student12
1013 student13
1014 student14
1015 student15
1016 student16
(2)导入数据到分桶表中
说明:Hive新版本load数据可以直接跑MapReduce,老版的Hive需要将数据传到一张表里,再通过查询的方式导入到分桶表里面。
hive (default)>
load data local inpath ‘/opt/module/hive/datas/student.txt’
into table stu_buck;
文件格式和压缩
text file和sequence file都是基于行存储的,orc和parquet是基于列式存储的。
Hadoop压缩概述
压缩格式 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|
DEFLATE | DEFLATE | .deflate | 否 |
Gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | .bz2 | 是 |
LZO | LZO | .lzo | 是 |
Snappy | Snappy | .snappy | 否 |
压缩性能的比较:
压缩算法 | 原始文件大小 | 压缩文件大小 | 压缩速度 | 解压速度 |
---|---|---|---|---|
gzip | 8.3GB | 1.8GB | 17.5MB/s | 58MB/s |
bzip2 | 8.3GB | 1.1GB | 2.4MB/s | 9.5MB/s |
LZO | 8.3GB | 2.9GB | 49.3MB/s | 74.6MB/s |
为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器,如下表所示:
Hadoop查看支持压缩的方式hadoop checknative。
Hadoop在driver端设置压缩。
压缩格式 | 对应的编码/解码器 |
---|---|
DEFLATE | org.apache.hadoop.io.compress.DefaultCodec |
gzip | org.apache.hadoop.io.compress.GzipCodec |
bzip2 | org.apache.hadoop.io.compress.BZip2Codec |
LZO | com.hadoop.compression.lzo.LzopCodec |
Snappy | org.apache.hadoop.io.compress.SnappyCodec |
以TextFile为例
若一张表的文件类型为TextFile,若需要对该表中的数据进行压缩,多数情况下,无需在建表语句做出声明。直接将压缩后的文件导入到该表即可,Hive在查询表中数据时,可自动识别其压缩格式,进行解压。
需要注意的是,在执行往表中导入数据的SQL语句时,用户需设置以下参数,来保证写入表中的数据是被压缩的。
–SQL语句的最终输出结果是否压缩
set hive.exec.compress.output=true;
–输出结果的压缩格式(以下示例为snappy)
set mapreduce.output.fileoutputformat.compress.codec =org.apache.hadoop.io.compress.SnappyCodec;
2)ORC
若一张表的文件类型为ORC,若需要对该表数据进行压缩,需在建表语句中声明压缩格式如下:
create table orc_table
(column_specs)
stored as orc
tblproperties (“orc.compress”=“snappy”);
Hive的企业级调优(面试重点)
Flume
概述
组成
1.2.1 Agent
Agent是一个JVM进程,它以事件的形式将数据从源头送至目的地。
Agent主要有3个部分组成,Source、Channel、Sink。
1.2.2 Source
Source是负责接收数据到Flume Agent的组件。Source组件可以处理各种类型、各种格式的日志数据
1.2.3 Sink
Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent。
1.2.4 Channel
Channel是位于Source和Sink之间的缓冲区。因此,Channel允许Source和Sink运作在不同的速率上。Channel是线程安全的,可以同时处理几个Source的写入操作和几个Sink的读取操作。
Flume自带两种Channel:Memory Channel和File Channel。
Memory Channel是内存中的队列。Memory Channel在不需要关心数据丢失的情景下适用。如果需要关心数据丢失,那么Memory Channel就不应该使用,因为程序死亡、机器宕机或者重启都会导致数据丢失。
File Channel将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据。
1.2.5 Event
传输单元,Flume数据传输的基本单元,以Event的形式将数据从源头送至目的地。Event由Header和Body两部分组成,Header用来存放该event的一些属性,为K-V结构,Body用来存放该条数据,形式为字节数组。
事务
doCommit:检查channel内队列是否足够合并 的意思是 当前putlist中的数据是否可以放到channel中,可以就放,不可以就不放,然后都清空putList
https://flume.apache.org/releases/content/1.10.0/FlumeUserGuide.html
Kafka
消息队列,基础架构
- (1)Producer:消息生产者,就是向Kafka broker发消息的客户端。
- (2)Consumer:消息消费者,从Kafka broker取消息的客户端。
- (3)Consumer Group(CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
- (4)Broker:一台Kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
- (5)Topic:可以理解为一个队列,生产者和消费者面向的都是一个topic。
- (6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
- (7)Replica:副本。一个topic的每个分区都有若干个副本,一个Leader和若干个Follower。
- (8)Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是Leader。
Follower:每个分区多个副本中的“从”,实时从Leader中同步数据,保持和Leader数据的同步。Leader发生故障时,某个Follower会成为新的Leader。
生产者消息发送流程
生产者重要参数列表
参数名称 | 描述 |
---|---|
bootstrap.servers | 生产者连接集群所需的broker地址清单。例如hadoop102:9092,hadoop103:9092,hadoop104:9092,可以设置1个或者多个,中间用逗号隔开。注意这里并非需要所有的broker地址,因为生产者从给定的broker里查找到其他broker信息。 |
key.serializer和value.serializer | 指定发送消息的key和value的序列化类型。一定要写全类名。 |
buffer.memory | RecordAccumulator缓冲区总大小,默认32m。 |
batch.size | 缓冲区一批数据最大值,默认16k。适当增加该值,可以提高吞吐量,但是如果该值设置太大,会导致数据传输延迟增加。 |
linger.ms | 如果数据迟迟未达到batch.size,sender等待linger.time之后就会发送数据。单位ms,默认值是0ms,表示没有延迟。生产环境建议该值大小为5-100ms之间。 |
acks | 0:生产者发送过来的数据,不需要等数据落盘应答。1:生产者发送过来的数据,Leader收到数据后应答。-1(all):生产者发送过来的数据,Leader+和isr队列里面的所有节点收齐数据后应答。默认值是-1,-1和all是等价的。 |
max.in.flight.requests.per.connection | 允许最多没有返回ack的次数,默认为5,开启幂等性要保证该值是 1-5的数字。 |
retries | 当消息发送出现错误的时候,系统会重发消息。retries表示重试次数。默认是int最大值,2147483647。如果设置了重试,还想保证消息的有序性,需要设置MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION=1否则在重试此失败消息的时候,其他的消息可能发送成功了。 |
retry.backoff.ms | 两次重试之间的时间间隔,默认是100ms。 |
enable.idempotence | 是否开启幂等性,默认true,开启幂等性。 |
compression.type | 生产者发送的所有数据的压缩方式。默认是none,也就是不压缩。 支持压缩类型:none、gzip、snappy、lz4和zstd。 |
生产经验–提高吞吐量
生产经验–数据可靠性
ack应答
生产经验–数据去重
幂等性
1)幂等性原理
生产者事务
0.11版本的Kafka同时引入了事务的特性,为了实现跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer获得的PID和Transaction ID绑定。这样当Producer重启后就可以通过正在进行的Transaction ID获得原来的PID。
为了管理Transaction,Kafka引入了一个新的组件Transaction Coordinator。Producer就是通过和Transaction Coordinator交互获得Transaction ID对应的任务状态。
Transaction Coordinator还负责将事务所有写入Kafka的一个内部Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。
注意:提前开启幂等性!!!
生产经验–数据有序or怎么解决乱序
- Kafka 最多只保证单分区内的消息是有序的,所以如果要保证业务全局严格有序,就要设置 Topic 为单分区。
- 如何保证单分区内数据有序or怎么解决乱序?
其中参数134都是保证数据安全性的,2是保证顺序的
在生产者向kafka中传输数据时
在producer中就排好序seqnum,0-5给6-11
6-9均已传输完成,传输第10个batch时发生故障写入失败,从broker返回写入失败的消息,于是producer开始重新传输batch10
但此时batch11已发出,到broker时,broker发现batch11的编号是5,而batch9的seqnum是3,5-3 不等于1,那么broker就不让batch11写入,保证了数据的有序性
等到batch10重新发送的请求成功写入了之后再允许后面的batch写入
Kafka Broker工作流程
broker启动后在zk中注册(即在集群上启动kafka)
然后controller选举leader(leader只存在于同一分区的不同副本上),谁先注册,谁说了算(当leader)选举规则如图
由leader节点上的controller监听broker节点的变化
每台节点上的controller将节点信息上传到ZooKeeper,其他节点的controller从zk同步相关信息
假设broker1(leader)挂了,controller监听到节点变化就要获取 ISR 选举新的leader:broker0,更新leader和 ISR
broker 重要参数
参数名称 | 描述 |
---|---|
replica.lag.time.max.ms | ISR中,如果Follower长时间未向Leader发送通信请求或同步数据,则该Follower将被踢出ISR。该时间阈值,默认30s。 |
auto.leader.rebalance.enable | 默认是true。 自动Leader Partition 平衡。 |
leader.imbalance.per.broker.percentage | 默认是10%。每个broker允许的不平衡的leader的比率。如果每个broker超过了这个值,控制器会触发leader的平衡。 |
leader.imbalance.check.interval.seconds | 默认值300秒。检查leader负载是否平衡的间隔时间。 |
log.segment.bytes | Kafka中log日志是分成一块块存储的,此配置是指log日志划分 成块的大小,默认值1G。 |
log.index.interval.bytes | 默认4kb,kafka里面每当写入了4kb大小的日志(.log),然后就往index文件里面记录一个索引。 |
log.retention.hours | Kafka中数据保存的时间,默认7天。 |
log.retention.minutes | Kafka中数据保存的时间,分钟级别,默认关闭。 |
log.retention.ms | Kafka中数据保存的时间,毫秒级别,默认关闭。 |
log.retention.check.interval.ms | 检查数据是否保存超时的间隔,默认是5分钟。 |
log.retention.bytes | 默认等于-1,表示无穷大。超过设置的所有日志总大小,删除最早的segment。 |
log.cleanup.policy | 默认是delete,表示所有数据启用删除策略;如果设置值为compact,表示所有数据启用压缩策略。 |
num.io.threads | 默认是8。负责写磁盘的线程数。整个参数值要占总核数的50%。 |
num.replica.fetchers | 副本拉取线程数,这个参数占总核数的50%的1/3 |
num.network.threads | 默认是3。数据传输线程数,这个参数占总核数的50%的2/3 。 |
log.flush.interval.messages | 强制页缓存刷写到磁盘的条数,默认是long的最大值,9223372036854775807。一般不建议修改,交给系统自己管理。 |
log.flush.interval.ms | 每隔多久,刷数据到磁盘,默认是null。一般不建议修改,交给系统自己管理。 |
Leader和Follower故障处理细节
kafka文件存储机制
index:稀疏索引
log:offset,偏移量
timeindex:时间索引
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.log
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index --print-data-log
//从3往下的所有数据
kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic demo --partition 0 --offset 3
高效读写数据
消费者
消费者初始化流程
消费者消费详细流程
生产经验——分区的分配以及再平衡
就是coordinate 选出来的Leader 是如何将分区分配给组内的各个消费者的
maxwell
Maxwell 是由美国Zendesk公司开源,用Java编写的MySQL变更数据抓取软件。它会实时监控MySQL数据库的数据变更操作(包括insert、update、delete),并将变更数据以 JSON 格式发送给 Kafka、Kinesi等流数据处理平台。官网地址:http://maxwells-daemon.io/
Maxwell原理
将自己伪装成slave,并遵循MySQL主从复制的协议,从master同步数据。Maxwell的工作原理是实时读取MySQL数据库的二进制日志(Binlog),从中获取变更数据,再将变更数据以JSON格式发送至Kafka等流处理平台。
Maxwell如何实现断点续传?
在MySQL中维护了一个元数据库,在元数据库中有一个position表,在这里记录了监控的binlog位置。
datax
DataX 是阿里巴巴开源的一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。
源码地址:https://github.com/alibaba/DataX
下载地址:http://datax-opensource.oss-cn-hangzhou.aliyuncs.com/datax.tar.gz
Spark
分布式计算框架
分布式
多进程、多节点
伪分布式:多进程、单节点
vCore:虚拟核
单进程申请资源少
进程和线程:Java中进程和线程底层都是调用JVM来抢占资源(CPU)的
栈溢出:栈里东西太多了
栈内存溢出:内存东西太多了无法新建栈了
微服务架构(可以和分布式的主从架构结合使用)
为了避免网络拥挤最好不把两台服务器放一个机房
计算(让CPU执行操作指令)
单机计算
分布式计算:核心是切片(将数据切分后给各个节点计算)
框架Frame
框架:不完整的计算机程序,无法独立运行