一、Hive
一)计算资源调优
基于Hive on MR
1.1 Yarn资源配置
yarn-site.xml
一个NodeManager节点分配给Container使用的内存,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量(默认8G,3台服务器总内存Memory Total24G),一般设置为总内存的1/2~2/3
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>65536</value>
</property>
一个NodeManager节点分配给Container使用的CPU核数VCores Total,取决于NM所在节点的总CPU核数和该节点运行的其他服务
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>16</value>
</property>
单个Container能够使用的最大内存
<property>
<name>yarn.scheduler.maximum-allocation-mb</name>
<value>16384</value>
</property>
单个Container能够使用的最小内存
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>512</value>
</property>
1.2 MapReduce资源配置
主要包括Map Task的内存和CPU核数,以及Reduce Task的内存和CPU核数
单个Map Task申请的container容器内存大小,默认值1024,该值不能超出yarn.scheduler.maximum-allocation-mb和yarn.scheduler.minimum-allocation-mb规定的范围
set mapreduce.map.memory.mb=2048;
单个Map Task申请的container容器cpu核数,默认值为1,一般无需调整
单个Reduce Task申请的container容器内存大小,默认值为1024,不能超出yarn.scheduler.maximum-allocation-mb和yarn.scheduler.minimum-allocation-mb规定的范围
set mapreduce.reduce.memory.mb=2048;
单个Reduce Task申请的container容器cpu核数,默认值为1,一般无需调整
二)SQL执行计划调优
Explain执行计划
- TableScan:表扫描操作,通常map端第一个操作肯定是表扫描操作
- Select Operator:选取操作
- Group By Operator:分组聚合操作
- Reduce Output Operator:输出到 reduce 操作
- Filter Operator:过滤操作
- Join Operator:join 操作
- File Output Operator:文件输出操作
- Fetch Operator:客户端获取数据操作
2.1 HQL语法优化之分组聚合优化
2.2 HQL语法优化之Join优化
2.3 HQL语法优化之数据倾斜
2.4 HQL语法优化之任务并行度
2.5 HQL语法优化之小文件合并
2.6 其他优化
1)CBO优化
2)谓词下推
3)矢量化查询
4)Fetch抓取
5)本地模式
6)并行执行
7)严格模式
二、Kafka
待补充……
三、Spark
待补充……
四、Flink
一)资源配置调优
为任务分配合适的资源,在一定范围内,增加资源的分配与性能的提升是成正比的
- 标准的Flink任务提交脚本(Generic CLI 模式)
bin/flink run \
-t yarn-per-job \
-d \
-p 3 \ 指定并行度
-Dyarn.application.queue=default \ 指定yarn队列
-Djobmanager.memory.process.size=1024mb \ 指定JM的总进程大小 2~4G足够
-Dtaskmanager.memory.process.size=1024mb \ 指定每个TM的总进程大小 2~8G足够
-Dtaskmanager.numberOfTaskSlots=2 \ 指定每个TM的slot数 core:slot=1:1 或 1:2
-c com.atguigu.flink.tuning.UvDemo \
/opt/module/flink-1.17.0/flink-tuning-1.0-SNAPSHOT.jar
参数列表://nightlies.apache.org/flink/flink-docs-release-1.13/docs/deployment/config/
1.1 内存设置
1)TaskManager内存模型
- JVM特定内存
taskmanager.memory.jvm-metaspace.size,默认256mb
taskmanager.memory.jvm-overhead.fraction,默认0.1
taskmanager.memory.jvm-overhead.min,默认192mb
taskmanager.memory.jvm-overhead.max,默认1gb
- 框架内存
堆内:taskmanager.memory.framework.heap.size,默认128MB
堆外:taskmanager.memory.framework.off-heap.size,默认128MB
- Task内存
堆内:taskmanager.memory.task.heap.size,默认none,由Flink内存扣除掉其他部分的内存得到
堆外:taskmanager.memory.task.off-heap.size,默认0,表示不使用堆外内存
- 网络内存(堆外)
taskmanager.memory.network.fraction,默认0.1
taskmanager.memory.network.min,默认64mb
taskmanager.memory.network.max,默认1gb
- 托管内存/管理内存(堆外)
taskmanager.memory.managed.fraction,默认0.4,非RocksDB时可以调小
taskmanager.memory.managed.size,默认none
1.2 CPU资源
Yarn的容量调度器默认情况下是使用“DefaultResourceCalculator”分配策略,只根据内存调度资源
可以在capacity-scheduler.xml 中修改属性
<property>
<name>yarn.scheduler.capacity.resource-calculator</name>
<!-- <value>org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator</value> -->
<value>org.apache.hadoop.yarn.util.resource.DominantResourceCalculator</value>
</property>
- 默认策略:DefaultResourceCalculator
- 动态策略:DominantResourceCalculator
- 默认一个slot对应一个CPU vcore,可以手动指定为一个slot对应任意个CPU vcore
- 用户设置的参数不会影响JobManager-->仍然对应1 vcore(ApplicationMaster)
- Running Containers = 1 JobManager + numberOfTaskSlots
- Allocated CPU VCores = 1 JobManager + numberOfTaskSlots * vcores
- 默认一个slot对应一个CPU vcore,可以手动指定为一个slot对应任意个CPU vcore
不同共享组的最大并行度之和 = slot个数
1 CPU => 4G内存
资源充足 => CPU:slot = 1:1
资源不足 => CPU:slot = 1:2 1:3
1.3 并行度设置
先压测,测试单个并行度的处理上限,再计算并行度 = 总QPS/单并行度的处理能力
一般,整个任务的并行度 = Kafka的分区数
举例:
Kafka主题 4个分区
4个分区 ==> 并行度为4 ==> 4个slot ==> CPU:slot=1:2 ==> 2个CPU ==> 8G内存(1个CPU 4G)
二)大状态及CheckPoint调优
2.1 RocksDB大状态调优
1)开启State访问性能监控(不常用)
state.backend.latency-track.keyed-state-enabled:true #启用访问状态的性能监控
-Dstate.backend.latency-track.keyed-state-enabled=true \
2)开启增量检查点和本地恢复(不开启则默认全量)
state.backend.incremental: true #默认false,改为true,开启增量检查点
state.backend.local-recovery: true #开启本地恢复,目前仅涵盖键控类型的状态后端(RocksDB)
3)调整预定义选项(常用)
当前支持的预定义选项有DEFAULT、SPINNING_DISK_OPTIMIZED、SPINNING_DISK_OPTIMIZED_HIGH_MEM或FLASH_SSD_OPTIMIZED
state.backend.rocksdb.predefined-options: SPINNING_DISK_OPTIMIZED_HIGH_MEM #设置为机械硬盘+内存模式
4)增大block缓存
state.backend.rocksdb.block.cache-size: 64m #默认8m,建议64~256mb
5)增大write buffer和level阈值大小
state.backend.rocksdb.writebuffer.size: 128m #默认64mb,建议调大
state.backend.rocksdb.compaction.level.max-size-level-base: 320m #默认64mb
6)增大write buffer数量
state.backend.rocksdb.writebuffer.count: 5 #默认2
7)增大后台线程数和write buffer合并数
state.backend.rocksdb.thread.num: 4 #默认为1
state.backend.rocksdb.writebuffer.number-to-merge: 3 #默认为1
2.2 CheckPoint设置
一般为min分钟级别,时效性要求高时可以设置为s秒或ms毫秒级
三)反压处理(重点)
Flink网络流控及反压的介绍:
https://flink-learning.org.cn/article/detail/138316d1556f8f9d34e517d04d670626
反压(BackPressure):生产速度 > 消费速度
危害:数据积压、延迟变高、资源耗尽、系统崩溃(影响checkpoint时长、影响state大小)
定位反压节点:禁用任务链,方便排查定位到具体算子
原因及处理:
同一个Task中不同SubTask的BackPressure
现象:有红有绿 ==> 原因:数据倾斜 ==> 解决:第四章
现象:全红 ==> 看busy
繁忙 ==> 原因:资源不足 ==> 解决:调整资源
不繁忙 ==> 原因:与外部交互 ==> 解决:加缓存Cache、异步IO
四)数据倾斜(重点)
数据倾斜不一定产生反压
keyBy前:
原因:一个并行度消费一个分区的数据 上游source分区中本身就是数据倾斜
解决:使用rebalance重分区(全局轮询)
keyBy后的聚合/窗口操作:
直接聚合:
加随机数实现双重聚合(× 来一条输出一条)
预聚合(√)==> 状态编程+定时器
开窗聚合:
加随机数实现双重聚合(√ 一个窗口输出一次 第一次开窗聚合后要添加窗口信息作为第二次分组条件)
预聚合(× 丢失了时间,丢失了数据条数)
五)Job优化
5.1 算子指定UUID(重点)
严格地说,仅需要给有状态的算子设置就足够了
但因为Flink的某些内置算子(如window)是有状态的,而有些是无状态的,可能不清楚哪些有状态的哪些没有
所以从实践经验上来说,建议每个算子都指定上 UUID
.uid("").name("")
5.2 链路延迟测量(不常用)
flink本身监控:不与数据库交互或维表关联时,延迟100ms以下(开窗时间另算)
开启参数:
metrics.latency.interval: 30000 #默认0,表示禁用,单位毫秒
metrics.latency.granularity: operator #默认operator
5.3 开启对象重用(存在风险)
给下游发送地址值
必须要确保下游Function只有一种,或者下游的Function均不会改变对象内部的值,否则可能会有线程安全的问题
env.getConfig().enableObjectReuse();
5.4 细粒度滑动窗口优化
细粒度滑动窗口:窗口大小远远大于步长
重叠的窗口过多,一个数据会属于多个窗口,性能会急剧下降
解决:滚动窗口+在线存储+读时聚合
六)FlinkSQL调优
FlinkSQL官网配置参数:
//nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/table/config/
6.1 设置空闲状态保留时间TTL(重点)
#API指定
tableEnv.getConfig().setIdleStateRetention(Duration.ofHours(1));
#参数指定
Configuration configuration = tableEnv.getConfig().getConfiguration();
configuration.setString("table.exec.state.ttl", "1 h");
// 指定时区
configuration.setString("table.local-time-zone", "Asia/Shanghai");
6.2 开启MiniBatch
MiniBatch:微批处理,缓存一定的数据后再触发处理,以减少对State的访问,从而提升吞吐并减少数据的输出量
// 开启miniBatch
configuration.setString("table.exec.mini-batch.enabled", "true");
// 批量输出的间隔时间
configuration.setString("table.exec.mini-batch.allow-latency", "5 s");
// 防止OOM设置每个批次最多缓存数据的条数,可以设为2万条
configuration.setString("table.exec.mini-batch.size", "20000");
适用场景:聚合,增加延迟换取高吞吐
6.3 开启LocalGlobal
前提是开启MiniBatch
// 开启LocalGlobal
configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE");
相当于预聚合
6.4 开启Split Distinct
去重,手动改写为两层聚合(增加按Distinct Key取模的打散层)
Split Distinct和LocalGlobal的原理对比
// 开启Split Distinct
configuration.setString("table.optimizer.distinct-agg.split.enabled", "true");
// 第一层打散的bucket数目
configuration.setString("table.optimizer.distinct-agg.split.bucket-num", "1024");
6.5 多维DISTINCT 使用Filter(特定语法优化)
CASE WHEN语法
SELECT
a,
COUNT(DISTINCT b) AS total_b,
COUNT(DISTINCT CASE WHEN c IN ('A', 'B') THEN b ELSE NULL END) AS AB_b,
COUNT(DISTINCT CASE WHEN c IN ('C', 'D') THEN b ELSE NULL END) AS CD_b
FROM T
GROUP BY a
在上面的示例中,三个COUNT DISTINCT都作用在b列上,此时,经过优化器识别后,Flink可以只使用一个共享状态实例,而不是三个状态实例,可减少状态的大小和对状态的访问
将上边的CASE WHEN替换成FILTER后
SELECT
a,
COUNT(DISTINCT b) AS total_b,
COUNT(DISTINCT b) FILTER (WHERE c IN ('A', 'B')) AS AB_b,
COUNT(DISTINCT b) FILTER (WHERE c IN ('C', 'D')) AS CD_b
FROM T
GROUP BY a
七)常见故障排除
7.1 非法配置异常
异常:从TaskExecutorProcessUtils 或 JobManagerProcessUtils抛出的IllegalConfigurationException
原因:通常表明存在无效的配置值(例如负内存大小、大于 1 的分数等)或配置冲突
解决:重新配置内存参数
7.2 内存
1)Java堆空间异常
异常:OutOfMemoryError:Java heap space
原因:通常表示JVM Heap太小
解决:可以尝试通过增加总内存来增加JVM堆大小,也可以直接为TaskManager增加任务堆内存,或为JobManager增加JVM堆内存,在确定Flink框架本身需要更多内存时还可以为TaskManagers增加框架堆内存
2)直接缓冲存储器异常
异常:OutOfMemoryError:Direct buffer memory
原因:通常表示JVM直接内存限制太小或存在直接内存泄漏
解决:检查用户代码或其他外部依赖项是否使用了JVM直接内存,以及它是否被正确考虑,可以尝试通过调整直接堆外内存来增加其限制
3)元空间异常
异常:OutOfMemoryError:Metaspace
原因:通常表示JVM元空间限制配置得太小
解决:加大JVM元空间TaskManagers或JobManagers选项
4) 网络缓冲区数量不足
异常:IOException: Insufficient number of network buffers
原因:通常表示配置的网络内存大小不够大
解决:增加网络内存
5)超出容器内存异常
异常:Flink 容器尝试分配超出其请求大小(Yarn或Kubernetes)的内存
原因:没有预留足够的本机内存
解决:调整JVM OverHead内存
7.3 Checkpoint
1)Checkpoint Decline
异常:当前task接收到上游发送过来的barrierCancel消息,从而取消了对应的Checkpoint
从 jobmanager.log 中看到类似下面的日志:
execution 被调度:
Decline checkpoint 10423 by task 0b60f08bf8984085b59f8d9bc74ce2e1 of job 85d268e6fbc19411185f7e4868a44178.
Checkpoint Cancel:
当前 Flink 中如果较小的 Checkpoint 还没有对齐的情况下,收到了更大的 Checkpoint,则会把较小的 Checkpoint 给取消掉
$taskNameWithSubTaskAndID: Received checkpoint barrier for checkpoint 20 before completing current checkpoint 19. Skipping current checkpoint.
在下游 task 收到被 cancelBarrier 的时候:
DEBUG
$taskNameWithSubTaskAndID: Checkpoint 19 canceled, aborting alignment.
或者
DEBUG
$taskNameWithSubTaskAndID: Checkpoint 19 canceled, skipping alignment.
或者
WARN
$taskNameWithSubTaskAndID: Received cancellation barrier for checkpoint 20 before completing current checkpoint 19. Skipping current checkpoint.
2)Checkpoint Expire
异常:Checkpoint 由于超时而失败
Checkpoint 1 of job 85d268e6fbc19411185f7e4868a44178 expired before completing.
3)Checkpoint 慢
异常:Checkpoint interval 1分钟,超时10分钟,Checkpoint经常需要做 9 分钟(我们希望1分钟左右就能够做完)
原因及解决:
-
① Source Trigger Checkpoint 慢
-
原因:source 做snapshot并往下游发送barrier的时候,需要抢锁,一直抢不到锁的话,可能导致Checkpoint一直得不到机会进行
-
解决:通过 jstack 进行进一步确认锁的持有情况
② 使用增量checkpoint
原因:仅在 RocksDBStateBackend 中支持增量 Checkpoint
解决:如果使用 RocksDBStateBackend,可以通过开启增量 Checkpoint 来加速
③ 作业存在反压或数据倾斜
原因:task 仅在接受到所有的barrier之后才会进行snapshot,如果作业存在反压,或者有数据倾斜,则会导致全部的channel或者某些channel的barrier发送慢,从而整体影响Checkpoint 的时间
解决:见反压、数据倾斜
④ barrier对齐慢
原因:taskmanager.log 中没有日志
DEBUG
Starting checkpoint (6751) CHECKPOINT on task taskNameWithSubtasks (4/4)
解决:检查日志,了解哪些上游的 barrier 没有发送下来
⑤ 主线程太忙导致没机会做snapshot
原因:在 task 端,所有的处理都是单线程的,数据处理和 barrier 处理都由主线程处理,如果主线程在处理太慢(比如使用 RocksDBBackend,state 操作慢导致整体处理慢),导致 barrier 处理的慢,也会影响整体 Checkpoint 的进度
解决:通过火焰图分析
⑥ 同步阶段做得慢
⑦ 异步阶段做得慢
7.4 Kafka动态发现分区
通过 Properties指定参数开启(单位是毫秒):
FlinkKafkaConsumerBase.KEY_PARTITION_DISCOVERY_INTERVAL_MILLIS
7.5 WaterMark不更新
原因:由于下游算子 watermark 的计算方式是取所有不同的上游并行数据源 watermark 的最小值,则其 watermark 将不会发生变化,导致窗口、定时器等不会被触发
解决:使用 WatermarkStrategy 来检测空闲输入并将其标记为空闲状态
.withIdleness(Duration.ofMinutes(5))
7.6 依赖冲突
异常:ClassNotFoundException/NoSuchMethodError/IncompatibleClassChangeError/...
原因:用户依赖第三方包的版本与Flink框架依赖的版本有冲突
解决:根据报错信息中的类名,定位到冲突的jar包,idea可以借助maven helper插件查找冲突的有哪些,打包插件建议使用maven-shade-plugin
7.7 超出文件描述符限制
异常:java.io.IOException: Too many open files
原因:超出文件描述符限制
解决:检查Linux系统ulimit -n的文件描述符限制,再注意检查程序内是否有资源(如各种连接池的连接)未及时释放
低版本Flink使用RocksDB状态后端也有可能会抛出这个异常,此时需修改flink-conf.yaml中的state.backend.rocksdb.files.open参数,如果不限制,可以改为-1(1.13默认就是-1)
7.8 脏数据导致数据转发失败
异常:org.apache.flink.streaming.runtime.tasks.ExceptionInChainedOperatorException: Could not forward element to next operator
原因:程序业务逻辑有误,或数据流里存在未处理好的脏数据
解决:继续向下追溯异常栈一般就可以看到具体的出错原因,比较常见的如POJO内有空字段,或者抽取事件时间的时间戳为null
7.9 通讯超时
异常:akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://...]] after [10000 ms]
原因:集群负载较大、网络拥堵,或业务逻辑同步调用耗时的外部服务
解决:调大akka.ask.timeout参数的值(默认只有10秒),调用外部服务时尽量异步操作(Async I/O)
7.10 其他错误
Flink on YARN(下):常见问题与排查思路-阿里云开发者社区
发现问题:
prometheus:监控
grafana:可视化展示&告警
之后通过webUI定位问题