spark的优化
1. 开发调优:
1) 避免创建重复的RDD
2) 尽可能复用同一个RDD
3) 对多次使用的RDD进行持久化
4) 尽量避免使用shuffle类算子
5) 使用map-side 的预聚合的shuffle
6) 使用高性能的算子
7) 广播大变量
8) 使用Kryo优化序列化性能
9) 优化数据结构
2. 资源调优
1) 资源参数调优
Num-executors
excutor-memory
executor-cores
Driver-memory
Spark.default.parallelsm
spark.storage.memoryFraction
spark.shuffle.memoryFraction
3. 数据倾斜调优
1) Hive ETL 预处理
2) 过滤少数导致倾斜的Key
3) 提高shuffle操作的并行度
4) 两阶段的聚合(局部聚合+全局聚合)
5) 将 reduce join转为 map join
6) 采样倾斜Key 并拆分join操作
7) 使用随机前缀和扩容RDD进行join
8) 多种方案组合使用
4. Shuffle调优
Spark.shuffle.file.buffer
spark.reducer.maxSizeInFlight
spark.shuffle.io.maxRetries
spark.shuffle.io.retryWait
spark.shuffle.memoryFraction
spark.shuffle.manager
spark中的partition,task,stage等关系
1. 在HDFS上存储的文件File一般有多个块,成为block。大小一般128M
2. Task 与 partition是对等的。 一个partition 对应(=) 一个task。输入的文件被划分成多个partition进行计算。
3. 一个executor有几个core,则有几个task被并行执行。一个core在一个时间内只能执行一个task任务。
4. 一个物理节点可以有多个worker;一个worker可以开启多个 executor。
5. Task被执行的并发度 = Executor数目 * 每个Executor核数。
6. stage 是以 shuffle为界限划分。一次shuffle是一次stage,所以stage次数 = shuffle次数 + 1。
7. 一个job 由多组task组成,每组任务被称为一个stage
Spark stream 的batch duration,window duration, slide duration
1. Batch Duration: 批处理间隔, 是指Spark streaming以多少时间间隔为单位提交任务逻辑
2. Window Duration:当前一个窗口处理数据的时间跨度。控制每次计算最近的多少个批次的数据。
3. Slide window:控制着计算的频率。用来控制对新的 DStream 进行计算的间隔。
Sql 中的几种join 操作含义
1. Inner join: 如果表中有至少一个匹配,则返回行 -- 并集返回
2. Left join: 即使右表中没有匹配,也从左表返回所有的行 -- 左侧全返回,右侧不存在用null代替
3. RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行 -- 右侧全返回,左侧不存在用null代替
4. FULL JOIN: 只要其中一个表中存在匹配,就返回行 -- 左右都返回,不匹配用null表示。
Kafka关于数据流,手动处理offset的问题来处理数据流的问题
1. 有且仅有一次:基于数据库事务将offset保存到第三方数据库,可以实现有且仅有一次。
2. 至少一次:业务处理完成后,再将offset异步提交到kafka上。
3. 至多一次:在业务处理之前,先将offset提交到kafka上。
Kafka Producer 三种发送消息的方式
1. Fire-and-forget --- 此方法用来发送消息到broker,不关注消息是否成功到达。大部分情况下,消息会成功到达broker,
因为kafka是高可用的,producer会自动重试发送。但是,还是会有消息丢失的情况;
2. Synchronous Send(同步发送) --- 发送一个消息,send()方法返回一个Future对象,使用此对象的get()阻塞方法可以查看send()方法是否执行成功。
3. Asynchronous Send(异步发送) --- 以回调函数的形式调用send()方法,当收到broker的响应,会触发回调函数执行(Callback)。
Kafka acks参数的含义
acks=0:Producer不会等待broker的回复,Producer会假定消息已成功发送,立即返回。因此可以使用此设置来实现非常高的吞吐量。
acks=1:当leader副本收到消息时,Producer将从Broker接收到成功响应。acks=1时的吞吐量,取决于消息是同步发送还是异步发送。
acks=all/-1:一旦所有处于同步状态的副本收到消息,producer将收到来自broker的成功响应,此种情况,消息延迟会更高。
Kafka key的作用
可以用于存储消息的额外信息
用来决定消息将被写到哪个topic分区中
kafka相关术语
Broker : 一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
Topic : 一类消息,可以包括多个partition。
Producer : 消息生产者,就是向kafka broker推送消息的客户端。
Consumer : 消息消费者,向kafka broker拉取消息的客户端
Consumer Group (CG): 一组 Consumer 组成的,用来消费 一个 Topic 上不同的 partition。
Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。 kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。
Offset:每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息。
Kafka 常见动态参数配置
unclean.leader.election.enable:不严格的leader选举,有助于集群健壮,但是存在数据丢失风险。
min.insync.replicas:如果同步状态的副本小于该值,服务器将不再接受request.required.acks为-1或all的写入请求。
max.message.bytes:单条消息的最大长度。如果修改了该值,那么replica.fetch.max.bytes和消费者的fetch.message.max.bytes也要跟着修改。
cleanup.policy:生命周期终结数据的处理,默认删除。
flush.messages:强制刷新写入的最大缓存消息数。
flush.ms:强制刷新写入的最大等待时长。
Kafka的负载均衡的实现
由于Leader的主要角色是执行分区的所有读和写请求的任务,而Follower则是被动地复制Leader。因此,当Leader失败的时候,一个Follower接管了Leader的角色。整个过程确保了服务器的负载平衡。
Replicas和ISR的作用是什么
在创建Topic时,可以指定分区的副本Replicas个数。用于保证消息不丢失。Replica的个数需要小于等于Broker的个数,所有Partition的Replica默认情况会均匀分布到Broker上。
ISR是指处于同步状态的副本,是一组同步到leader的消息副本。用于保证消息的一致性。
消息传递方法有哪些类型?
队列:在这种方法中,一组用户可以从服务器顺序读取消息。
(传统的队列系统——它通常在处理完成后从队列的末尾删除消息。
Apache Kafka——但是在Kafka中,消息在被处理后仍然存在。这意味着kafka中的信息不会在消费者收到时被删除。)
发布-订阅:消息是向所有消费者广播的。
Kafka调优
Kafka调优可以从三方面考虑:
Kafka Producer调优(消息压缩、批次大小、同步或异步发送),结合Producer配置项一起学习。
Kafka Brokers 调优(设置合适的topic partition个数和副本个数,一般情况下,分区个数和副本个数与kafka集群节点个数相同。同时要考虑消费者的个数以及物理磁盘个数。)
Kafka Consumers调优(为消费组中增加消费者,但是不能大于分区数。选择合适的消费语义。)
HBase组件概念
Regionserver: 是存储Hbase的数据的单位
Hmaster: 进行负载均衡的功能, 决定 region 放在哪里.
HRegion:存放hbase中数据的一个概念,可以简单的理解为表,存放一张表中的一部分数据。
HFile: 在hdfs上存放数据之前的一个物理结构,用于接收从客户端提交过来的数据。
Hbase表设计关键点
1. RowKey长度原则:最大长度为64KB,实际应用中一般为10~100bytes,一般设计成定长。建议是越短越好
2. RowKey散列原则:数据存储是按照rowkey字符串的字典顺序,rowkey过于连续会造成存储热点,需要离散存储,就需要把rowkey打散。可以通过对rowkey做reverse操作。
3. RowKey唯一原则:必须在设计上保证其唯一性。
CAP是啥
一致性:所有节点在同一时间数据相同
可用性:有请求有响应,但是不保障每次请求获取的数据正确
分区容错性:系统在任意时刻的数据丢失不能影响整个分布式系统的继续运作。
牺牲一致性保证可用性。
HBASE 读流程
1. 首先从 zk 中找到 meta 表 region 的位置, 然后从 meta 表中获取用户表的region信息
2. 根据 namespace, 表名和 rowkey 在 meta 表中找到对应的 region 信息, 找到对应的regionserver.
3. 定位 region
4. 从 Memstore 找到数据, 如果没有, 再到 storefile 上读取 (为了读取的效率)
HABSE 写流程
1. 通过 zookeeper 找到 meata表的region
2. 根据相关信息找到 regionserver
3. 把数据分别写到 HLog 和 memstore 中.
HBase 避免热点问题
某一段时间内,Hbase读写集中在少部分Region上,负载明显过大,其他RegionServer闲置,叫做热点现象.
方案:RowKey设计,预分区,列簇设计,索引表
HBase 性能调优
预分区:对应数据需要写到对应的region中,这个也解决了数据倾斜
Rowkey优化:(QA:Hbase表设计关键点)
Column优化:列族的名称和列的描述名字要短,同一张的ColumnFamilly不要超过3个
Schema优化:宽表和高表(业务场景平衡)事务性、查询性能 、损耗资源等等
Storm处理三种消息的处理模式
at most once:若不实现ack和fail方法,无论后续处理结果如何,消息只会发送一次,必定不能满足高准确性;
at least once:若实现了ack和fail方法,只有调用了ack方法才会任务处理成功,否则会重试。可能会出现消息重复,在并发场景下重复又意味着可能出现乱序;
exactly once:trident每个micro batch作为整体只成功处理一次,但也是无法保证消息真的只正确的处理一次,比如数据已经处理完毕并持久化,但向数据源ack时失败,就可能会有重试。
SparkSQL程序执行过程
1、 先写Dataset API SQL代码
2、 如果代码没有编译错误,Spark会将代码转换为逻辑计划
3、 Spark会将逻辑计划转换为物理计划,会对代码进行优化(catalyst优化器)
4、 Spark会执行物理计划( RDD )
MapReduce 的操作
Map 任务执行结果输出写入到本地操盘,而不是HDFS。因为:一旦任务完成,映射输出可以扔掉了。所以,复制并将其存储在HDFS变得大材小用。
HDFS:一次写入,多次读取。
storm 的Executor 与Task的关系
1. setNumWorker(2) => setBolt(**,**,4) => setNumberTask(8);
2: 两个work进程;4:四个Executor线程,8个Task。
2. 一个Executork可以有多个或等于Task,一个Task对应一个topology。
3. Work属于进程级别;Executork是线程级别;task是topology级别。
4. Work孵化出Executork进程,一个work可以对应多个Executork。
4. Executork,task是可以动态调节的。
5. Executor的数目 = 组件数 + 隐含的ACK数量。
RDD (Resilient Distributed Dataset)的特点
内存计算、 延迟计算、 容错性、 不可变性、 分区、 持久化、 数据本地性
Spark 分区
1. 在集群环境中,读取本地文件/HDFS数据,spark的partition由数据的block个数决定,最小为2
2. 每个partition会运行一个task来处理其中的数据元素
3. Partition(分区)亦是并行度,所有work执行的一次执行task的数量
如果spark-default.conf或SparkConf中设置了spark.default.parallelism参数值,
那么spark.default.parallelism=设置值。
如果未设置,在yarn与standalone模式中:spark.default.parallelism = max(所有executor使用的core总数, 2)。
即可以RDD的分区数:sc.defaultParallelism = spark.default.parallelism
Spark.default.parallelism 属性值设置为: 单个work节点计算线程 * work节点个数 * 3
Spark 分区的选择
分区太少: 1. 减少并发性;
2. 数据倾斜和不恰当的资源利用
分区太多: 1. 任务调度可能比实际执行时间花费更多的时间
RDD 分区数的选择: 可用core数量(单个work节点core线程个数 * work节点个数)的2-3倍。
单个分区的数据量大小最终取决于执行程序的可用内存。
Tips:WebUI上查看任务执行,至少需要100+ ms时间。如果所用时间少于100ms,那么应用程序可能会花更多的时间来调度任务。此时就要减少partition的数量。
Spark 应用程序的调度
Driver会向master申请资源,master收到请求之后,会向worker进行资源调度,启动Executor,然后,Executor向Driver进行注册。此时Spark 应用程序就会知道哪些worker上面的executor已经就绪。
接着开始执行spark任务:遇到action操作创建一个job—提交给DAGScheduler, DAGScheduler会把job分为多个stages(shuffle:最后一个stage里面的task叫ResultTask,前面stage里面的task叫shuffleMapTask。),为每个stage创建一个taskset集合,集合中的task计算逻辑完全相同,只是处理的数据不同。然后DAGScheduler会把taskset交给TaskScheduler,TaskScheduler会把taskset里面的task发送给Executor。Executor接收到task,会启动一个线程池TaskRunner,在里面运行task。
实例化SparkContext是在Driver端完成的,后续的代码操作都是在work端进行完成的。
文件收集框架Flume
实时收集数据,经常与storm/spark集成进行使用
Flume只有一个角色的节点Agent,其三大组件为:
收集collecting source
聚合aggregating channel
移动moving sink
Event是Flume数据传输的基本单元,Flume以event 的形式将数据从源头传送到最终的目的
Flume提供了三种方式处理此种错误
End-to-end:收到数据agent首先会把数据写到磁盘,等待传输成功后再删除,如果传输失败,再次发送
Store on failure:若接收方crash,再把数据写到本地,等待对方恢复之后继续发送
Besteffort:等待数据发送到接收方之后,不会进行确认
HDFS分布式文件系统对数据 写入过程
1. DFSClient发消息给NameNode,表示将数据文件(需要计算的文件)写入
2. NameNode发消息给DFSClient,让DFSClient写到DataNodeA、B和D,并直接联系DataNodeB
3. DFSClient发消息给DataNodeB,让它保存一份数据文件. (同机架或不同机架)
4. DataNodeB在保存数据文件时,并将数据文件发送个DataNodeA和DataNodeD,进行备份
6. DataNodeB发确认消息给DFSClient与NameNode,表示写入完成
HDFS分布式文件系统对数据 读取过程
1. DFSClient询问NameNode应该从哪里读取文件.
2. NameNode发送 “数据块”的信息给DFSClient。
3. DFSClient检查数据块信息,联系相关的DataNode,请求数据块
4. DataNode返回文件内容给DFSClient,然后关闭连接,完成读操作
yarn 主要组件
ResourceManager: 主节点,处理客户端的请求、调度应用管理者、集群整体资源分配
NodeManager:从节点,单节点的资源管理、处理主节点/应用管理者的命令
ApplicationMaster: 应用管理者,数据切分、应用程序资源的申请,分配资源给内部任务任务监控与容错
Container: 容器,任务运行环境的抽象, 封装了任务所需的所有资源、资源隔离的效果
yarn 的业务流程
1.客户端向ResourceManager提交一个作业,ResourceManager则会为这个作业分配一个Container.
2.所以ResourceManager会与NodeManager进行通信,要求这个NodeManager启动一个Container.
3.而这个Container是用来启动ApplicationMaster的,ApplicationMaster启动完之后会与ResourceManager进行一个注册.
4.这时候客户端就可以通过ResourceManager查询作业的运行情况了.
5.然后ApplicationMaster还会到ResourceManager上申请作业所需要的资源,申请到以后就会到对应的
NodeManager之上运行客户端所提交的作业.
6.最后NodeManager就会把task运行在启动的Container里
Hive 表分类
内部表:删除内部表会直接删除元数据 (metadata) 及存储文件数据
外部表:被 external 修饰的表,删除外部表仅仅删除元数据, 文件不会被删除
分区表:把数据分类放在不同的目录下面, 加快查询速度
SQL 的执行顺序
第一步:执行FROM
第二步:WHERE条件过滤
第三步:GROUP BY分组
第四步:执行SELECT投影列
第五步:HAVING条件过滤
第六步:执行ORDER BY 排序
Hive中出现数据倾斜如何处理
1. 调节参数(在map中会做部分聚集操作 hive.map.aggr=true)
2. SQL 语句调节
3. 表的设计
Hive 调优
表拆分、MR优化配置、并行计算、JVM重用、本地计算、推测执行
HBase,Redis, MongDB 的应用场景
MongoDB是高性能、无模式的文档型数据库,支持二级索引,非常适合文档化格式的存储及查询。MongoDB的官方定位是通用数据库,确实和MySQL有些像,现在也很流行,但它还是有事务、join等短板,在事务、复杂查询应用下无法取代关系型数据库。
Redis是内存型Key/Value系统,读写性能非常好,支持操作原子性,很适合用来做高速缓存。
HBase存储容量大,一个表可以容纳上亿行、上百万列,可应对超大数据量要求扩展简单的需求。Hadoop的无缝集成,让HBase的数据可靠性和海量数据分析性能(MapReduce)值得期待。
Redis 主从同步步骤
1. slave 服务器连接 master 服务器, 并发送 psync 同步码.
2. master 服务器收到 psync 同步码, 执行 bgsave 生成snapshot(内存中,rdb), 并在缓存区记录从当前开始执行的所有写命令.
3. master 服务器在 bgsave 命令执行完成后, 将 snapshot 发送到 slave 服务器, slave 载入快照.
4. slave 服务器收到快照, 载入到内存中. 将自己的数据库状态更新至主服务器执行 BGSAVE 命令时的数据库状态. (rdb属于内存性的文件, 所以每次都会丢弃旧 rdb)
5. master 服务器发送完成 snapshot 后, 向 slave 服务器发送缓存区的写命令.(注意时间点)
6. salve 服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
Redis 单线程模型每秒万级别处理能力的原因
1. 纯内存访问。数据存放在内存中,内存的响应时间大约是100纳秒,这是 Redis 每秒万亿级别访问的重要基础。
2. 非阻塞 I/O,Redis 采用 epoll 做为 I/O 多路复用技术的实现,再加上 Redis 自身的事件处理模型将 epoll 中的连接,读写,关闭都转换为了时间,不在 I/O 上浪费过多的时间。
3. 单线程避免了线程切换和竞态产生的消耗。
4. Redis 采用单线程模型,每条命令执行如果占用大量时间,会造成其他线程阻塞,对于 Redis 这种高性能服务是致命的,所以 Redis 是面向高速执行的数据库。
分布式锁机制: 悲观锁, 乐观锁
悲观锁 |
乐观锁 |
悲观锁采用相对保守的策略,在资源争用比较严重的时候比较合适。悲观锁在事务开始之前就去尝试获得写权限,事务结束后释放锁;也就是说对于同一行记录,只有一个写事务可以并行(加锁,只有我能操作). |
乐观锁是在提交事务之前,大家可以各自修改数据,但是在提交事务的时候,如果发现在这个过程中,数据发生了改变,那么直接拒绝此次事务提交。(允许大家都操作,只是在提交事务中检测)乐观锁适合在资源争用不激烈的时候使用。 |
当前只能有写权限的用户可以操作, 而其他用户此时被拒绝. 如 synchronized (不允许写, 更不会执行) |
当前所有用户都可以操作, 但是在提交时会被拒绝. 如watch - multi - exec (允许写, 但不一定可以执行.) |
MongoDB的分片
分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。 通过一个名为mongos的路由进程进行操作,mongos知道数据和片的对应关系(通过配置服务器)。大部分使用场景都是解决磁盘空间的问题,对于写入有可能会变差(+++里面的说明+++),查询则尽量避免跨分片查询。使用分片的时机:
1. 机器的磁盘不够用了。使用分片解决磁盘空间的问题。
2. 单个mongod已经不能满足写数据的性能要求。通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。
3. 想把大量数据放到内存里提高性能。和上面一样,通过分片使用分片服务器自身的资源。