301. TableFactory允许从基于字符串的属性创建不同的表相关实例。调用所有可用工厂以匹配给定的属性集和相应的工厂类。利用Java spi进行发现。
302. TableFactory接口或特质的方法有requiredContext和supportedProperties
303. 可以自定义TableSourceFactory来返回StreamTableSource,比如实现StreamTableSourceFactory
304. sql客户端也能使用TableFactory,可在yaml配置
305. 可以自定义连接器,需要实现ConnectorDescriptor接口,然后在tableEnv.connect()中使用
306. table&sql的用户定义函数,包括ScalarFunction、TableFunction、AggregateFunction
307. 不要将TableFunction实现为scala object,因为它是单例的,会导致并发问题
308. AggregateFunction需要实现createAccumulator()、accumulate()、getValue()方法。根据用例,可能还需要retract()、merge()、resetAccumulator()方法
309. FunctionContext的方法有getMetricGroup、getCachedFile、getJobParameter
310. ScalarFunction的open方法,使用FunctionContext作为参数。而close方法用于清理。
311. start-cluster.sh用于启动具有worker的本地集群
312. sql-client.sh embedded用于启动嵌入式独立进程,或连接到远程sql客户端网关来启动sql client cli,会读取sql-client-defaults.yaml
313. cli支持两种维护和可视化结果的模式,set execution.result-mode=table和set execution.result-mode=changelog
314. sql查询需要一个执行它的配置环境,也叫做环境文件,是一个yaml文件
315. cli会话中设置的属性(例如,使用set命令)具有最高优先级。cli commands>session 环境文件>默认环境文件
316. 可以在环境文件指定更细粒度的重启配置,包括以下策略:fallback、none、fixed-delay、failure-rate
317. sql客户端不需要maven或sbt依赖,而是直接用--jar或--library来添加依赖项提交给集群
318. flink sql支持视图,可以在yaml中定义,如views:…,或在cli会话中定义,如create view…,且会话环境定义的视图具有最高优先级
319. datastream和dataset api统称为core api,而table api是一种声明式的DSL,所以table api比core api更简洁,代码更少,甚至会优化程序,但它的表现力不如core api
320. 问题:datastream只有transformation算子,没有action算子?
321. 算子子任务的数量是该特定算子的并行度。同一程序的不同算子可能具有不同的并行级别。
322. flink使用流重放和检查点的组合来实现容错。
323. 批处理的容错不使用检查点,完全通过重放流来恢复。
324. dataset引入了特殊的同步,即超步,来迭代,且这些迭代只能在有界流上进行。
325. flink包含两种类型的进程,jobManager和TaskManager,前者也叫master,后者也叫worker
326. 至少有一个jobManager,当HA时可以有多个。其中一个是leader,其他处于待机状态。TaskManager也至少有一个。
327. JM和TM可以通过多种方式启动,作为独立集群或在容器中,由yarn或mesos等资源框架管理
328. client、JM、TM之间通过actor system通信。client向JM提交job,或者取消job,而JM向client发送状态更新和统计结果。TM向JM发送心跳和任务状态,JM向TM部署/停止/取消task,以及检查点触发。TM之间进行数据流的交互和传输。当集群在yarn上部署时,JM也是yarn的AM
329. 一个TM有多个task slot,而一个task slot有多个task?
330. 每个worker/TM都是一个jvm进程。所以一个任务槽是一个线程?
331. 同一个jvm中的任务共享tcp连接(通过多路复用)和心跳消息。
332. 一个任务槽中的task可以是不同任务的子任务,只要它们来自同一个作业。
333. 多个任务共享插槽的好处是,可以提高资源利用率,确保繁重的子任务在TM之间公平分配。同时提高了并行度。
334. 注意,一个task就是一个线程,而一个task slot就是多个线程
335. 一个很好的默认任务槽数就是cpu核心数
336. 键值对可以保存在内存的哈希映射,或rocksdb中,而键值状态的时间点snapshot是检查点的一部分
337. 保存点是手动触发的检查点,它会获取程序的snapshot并将其写入状态后台。它们依靠常规的检查点机制
338. flink写入kafka需要加入flink-connector-kafka-xxx这个依赖项,然后addSink中使用FlinkKafkaProducer08
339. localhost:8081是flink的仪表板,可以查看集群资源和作业概述。还可以查看每个作业的视图,包括算子操作
340. 什么是反压(back pressure)?在localhost:8081上也能看到
341. pagerank通过DataSet的iterate得到IterativeDataSet,然后调用join/where/equalsTo/flatMap/groupBy/sum/map来重新得到Dataset,最后再调closeWith
342. 联通分量算法和pagerank类似,也是一种迭代算法。 核心方法是iterateDelta、getWorkset、closeWith
343. sbt的命令:
sbt new xxx
sbt clean assembly
sbt run
sbt的配置文件build.sbt
344. flink所有算子都是lazy的,也就是它没有action算子之说,最后通过execute触发执行
345. 问题:keyBy和groupBy的区别?datastream用前者,dataset用后者?
346. 字段表达式可轻松选择(嵌套)复合类型的字段,如Tuple、pojo类型。例如user、f0、user.zip、f1.user.zip、*等
347. 了解KeyedStream、keyBy、KeySelector、getKey,键选择器
348. org.apache.flinktypes.CopyableValue接口支持手动内部克隆逻辑。flink带有与基本类型相应的预定义值类型,如ByteValue、ShortValue、IntValue、CharValue等,充当它们的可变变体。
349. flink也支持Hadoop.Writable类型
350. flink还支持一些特殊类型,如scala的Either、Option、Try,以及java的Either,它可用于错误处理或输出两种不同类型记录的算子
351. flink实现了以下累加器,IntCounter、LongCounter、DoubleCounter,以及直方图,它们都实现了Accumulator接口。注意,累加器也需要注册,使用addAccumulator方法。最后使用getAccumulatorResult来获取结果。
352. scala版的dataset api有mapWith、mapPartitionwith、flatMapWith、filterWith、reduceWith、reduceGroupWith、groupingBy、sortGroupWith、combineGroupWith、projecting,这些方法用于支持匿名模式匹配
353. scala版的datastream api稍有不同,比如keyingBy、foldWith、applyWith
354. 需要加入flink.api.scala.extensions和flink.streaming.api.scala.extensions来进行扩展
355. 流计算的SourceFunction的子类有ParallelSourceFunction和RichParallelSourceFunction
356. writeUsingOutputFormat是流计算自定义输出的方法
357. 注意,write*()方法主要用于调试目的,它们没有参与flink的检查点。即具备at-least-once语义
358. 想可靠传输到文件系统,用flink-connector-filesystem,然后调用addSink方法,就能保证exactly-once
359. IterativeStream是迭代流,没有最大迭代次数,通过简单的map就能重新变为Datastream,最后调用closeWith来关闭迭代流
360. ExecutionConfig的setAutoWatermarkInterval用于设置自动水印发射的间隔
361. env.setBufferTimeout用于控制吞吐量和延迟,它设置缓冲区的最长等待时间
362. setBufferTimeout(-1)将删除超时,缓冲区只有在满时才会被刷新
363. DataStreamUtils.collect(datastream)可得到Iterator,叫做迭代器数据接收器
364. 水印是flink中用于衡量事件时间进度的机制。水印作为数据流的一部分流动,它带有时间戳t。一个水印宣布事件时间T到达,意味着该流后面没有更多数据元的时间戳T''<=T
365. 水印仅是流中的周期性标记;水印对于无序流是至关重要的;一旦水印到达算子,该算子就可以将其内部事件时钟提前到水印的值; 每个并行子任务通常独立地生成其水印
366. 有些事件可能会违反水印条件,即在水印t之后到达时间戳t"<=t的事件,这种情况如何处理?可以使用延迟数据元(allowed-lateness)
367. 除了纯事件时间水印分配器之外,还可以使用定期水印分配器,它不仅基于数据元时间戳进行分配,也可以在没有观察到新事件一段时间后切换到使用当前处理时间作为时间基础分配器。这就避免了没有要处理的数据元,导致水印不能进行的情况。
368. 算子需要在向下游转发之前完全处理给定的水印。细节见WindowOperator、OneInputStreamOperator的processWatermark以及TwoInputStreamOperator的processWatermark
369. 时间戳分配和生成水印紧密相关,水印告诉系统事件时间的进展
370. AssignerWithPeriodicWatermarks接口分配时间戳并定期生成水印。其中getCurrentWatermark是每次调用分配器的方法。而extractTimestamp是时间戳提取器。
371. 了解AssignerWithPunctuatedWatermarks接口和extractTimestamp方法、checkAndGetNextWatermark方法
372. 可以在每个事件上生成水印,但过多的水印会降低性能
373. 当kafka作为数据源时,每个kafka分区都有自己的事件时间模式(升序或无序时间戳)。当从kafka消费流时,多个分区并行消耗,交错来自分区的事件并破坏每个分区模式。
374. kafka分区可以感知水印生成,每个分区在kafka使用者内部生成水印。每个分区,水印的合并方式和在流上合并的方式相同。
375. flink具有水印合并机制,能够正确地合并kafka不同分区的水印。
376. BoundedOutOfOrdernessTimestampExtractor是允许固定数量的迟到的分配器
377. AscendingTimestampExtractor是具有递增时间戳的分配器
378. flink的state是专门为流计算服务的吗?不对,它也为CEP、flinkml、批处理服务。其中训练机器学习模型时,状态用来保存模型参数的当前版本。在CEP中,状态用来存储目前为止遇到的事件序列。
379. flink有两种基本的状态:keyed state和operator state
380. keyed state始终和键有关,只能在KeyedStream中使用。可看成已分区或分片的算子状态,每个key只有一个状态分区。被key化状态进一步组织成keys组,后者是flink重新分配key状态的原子单元
381. operator state即算子状态,或叫非keyed state,每个算子状态都绑定到一个并行算子实例。例如kafka的每个并行实例都将主题分区和偏移量的映射维护为其算子状态。
382. flink state有两种形式:托管状态和原始状态。前者指状态会存入内部哈希表或RocksDB,例如ValueState或ListState,最后会写入检查点。而后者指算子保存在自己的数据结构中的状态。flink对状态的数据结构一无所知,只看到原始字节。
383. 所有数据流函数都可以使用托管状态,但原始状态接口只能在实现算子时使用。建议使用托管状态(而不是原始状态),因为前者中,flink能够在并行性更改时自动重新分配状态,且还能进行更好的内存管理。
384. 托管的keyed state包括ValueState、ListState、ReducingState、AggregatingState、FoldingState、MapState,以上都能用clear()来清除状态,即输入数据元的键。
385. FoldingState和FoldingStateDescriptor已经在flink1.4中弃用,建议用AggregatingState以及AggregatingStateDescriptor;注意:要获得状态句柄,必须创建一个StateDescriptor
386. 流计算程序中自己维护状态(如ValueState)还是比较复杂的,需要用到update()、clear()、getRuntimeContext()、getState()、ValueStateDescriptor,然后外层定义RichFlatMapFunction的子类,并重写flatMap方法和open方法,最后在流计算中使用flatMap以及参数(即自定义的子类)
387. 状态有生存时间,即TTL,列表数据元和映射条目会独立过期。要使用TTL,必须先构建StateTtlConfig配置对象,具体是.newBuilder(…).setUpdateType(…).setStateVisibility(…).build()然后构建状态描述符,并设置ttl,具体是stateDescriptor.enableTimeToLive(ttlConfig)
388. UpdateType更新类型配置状态ttl刷新时,默认是OnCreateAndWrite;此外还有OnReadAndWrite选项
389. StateVisibility状态可见性配置的选项有NerverReturnExpired和ReturnExpiredIfNotCleanedUp,前者永远不返回过期,后者如果可用哪怕过期也可返回
390. TTL配置不是检查点或保存点的一部分,而是flink如何在当前运行作业中处理它的方式
391. StateTtlConfig的cleanupFullSnapshot()用来清理过期状态。注意,只会在读取过期值时才会清除,如调用value()方法。
392. 有状态函数可实现更通用的CheckpointedFunction接口和ListCheckpointed接口
393. CheckedpointFunction接口有snapshotState和initializaState两个方法,前者在检查点执行时调用,后者初始化用户定义函数时调用,也包括状态恢复逻辑。
394. ListCheckpointed接口有snapshotState和restoreState两个方法,前者返回检查点的对象列表,后者在恢复时处理这样的列表
395. 广播状态和其他算子状态的不同在于,它有一个map格式,它仅适用于具有广播流和非广播流的输入的特定算子,以及这样的算子可以有不同名称的多个广播状态
396. ruleStream.broadcast(ruleStateDescriptor)得到BroadcastStream<Rule>,即广播流和广播状态
397. 一般在非广播流上执行connect,而将广播流作为参数。然后调用process方法。后者参数可以是KeyedBroadcastProcessFunction实例或BroadcastProcessFunction实例。
398. BroadcastProcessFunction抽象类的方法有processElement和processBroadcastElement,前者是非广播流,后者是广播流。KeyedBroadcastProcessFunction也类似。
399. 广播状态的注意事项:
1) 没有跨任务通信
2) 广播状态的事件到达顺序因任务而异,因此状态更新不得取决于事件顺序
3) 所有任务都会检查它们的广播状态,来避免在恢复期间从同一文件读取所有任务(从而避免热点)
4) 广播状态保存在内存中,而不是RocksDB
400. 检查点允许flink恢复流中的状态和位置,为应用程序提供与无故障执行相同的语义。检查点机制需要一个持久的数据源,可以是消息队列或文件系统。状态的持久存储,通常是分布式文件系统。