- 采集侧流程
- 分发侧流程
- 怎么保证数据不重不丢
- canal的高可用需要注意的问题
采集侧基于什么实现?定制化开发实现了canal那部分?
canal部署架构
1.开发了一个定时任务,检查canal接口的采集实例是否存在 如果没有会一直尝试部署实例,保证采集数据不丢
2.增强了canal监控埋点
3.定制化了canal的globalId,开发的过程中涉及以下问题:
-
1、binlog globalid/group_id 含义
-
2、binlog pos 含义
-
3、binlog采集不会丢。怎么保障的?(canal-server的幂等性恢复,再次启动的时候从第一个binlig开始恢复)
-
4、如何保证采集高可用
-
Q &A 问题一
canal文档:github-wiki-see.page/m/alibaba/c…
globalid:基于binlog中提取的executeTime时间戳加上canal服务器系统的微秒时间戳得来, 这样的globid会是一个高精度,起到可以区分binlog日志先后执行顺序的作用; 将globid与日志数据包装好发送到kafka,最后落于中间表用于merge逻辑排序。
如果要改动globalId,那么需要涉及到canal解析链路的改动
canal的核心设计
- canal server:代表了我们部署的一个 canal 应用。
- canal instance:代表了一个 canal server 中的多个 MySQL instance。
- 一般情况下,一个 canal instance 对应一个 MySQL instance 这里说的不是我们在 MySQL 服务器上创建的一个数据库,而指的就是一个 MySQL 服务器实例
- 由此可以看出,一个 canal server 可以搜集多个数据库实例的数据, 在 canal 中把 instance 叫 destination。
- 每个 canal instance 由多个组件构成。 在 conf/spring/xxx-instance.xml 中配置了这些组件。
instance 模块(组件):
① eventParser(数据解析):模拟 MySQL slave 与 MySQL master 进行交互,负责 binary log 解析。
② eventSink(数据归集):Parser 和 Store 之间的链接器,负责对 eventParser 解析后的数据 进行过滤、加工、分发。类似于 ETL 中的 数据清洗。
③ eventStore(数据存储):用于存储 eventSink 处理后的数据。
④ metaManager:增量订阅 & 消费信息管理器。
- Q&A 问题2
canal采集位点流程图: www.processon.com/view/link/6…
在主从切换的时候,从库会向Mysql Master按照日志文件名(binlog文件名) 发送dump命令,当binlog的事件事件大雨待查找的时间戳 有两种选择 一种是开启事务,一种是从最近最新的消费位点开始消费
- Q&A 问题3 假设我们现在有一张表A,建表A与binlog如下 (一共有7个binlog)
CREATE TABLE t (
a INT(11) NOT NULL AUTO_INCREMENT,
b VARCHAR(20) DEFAULT NULL,
c INT,
PRIMARY KEY (a)
);
CREATE TABLE t1 LIKE t;
CREATE TABLE t2 LIKE t;
RESET MASTER;
INSERT INTO t(a,b,c) VALUES(1,’A’,100);
FLUSH LOGS;
binlog 2
INSERT INTO t(a,b,c) VALUES(2,’A’,200);
FLUSH LOGS;
binlog 3
INSERT INTO t(a,b,c) VALUES(3,’C’,100);
FLUSH LOGS;
binlog 4
DELETE FROM t WHERE b=’A’;
FLUSH LOGS;
....
解析如下
mysqlbinlog –skip-gtids -v mysql-bin.000001 > 1.sql
mysqlbinlog –skip-gtids -v mysql-bin.000002 > 2.sql
mysqlbinlog –skip-gtids -v mysql-bin.000003 > 3.sql
mysqlbinlog –skip-gtids -v mysql-bin.000004 > 4.sql
mysqlbinlog –skip-gtids -v mysql-bin.000005 > 5.sql
mysqlbinlog –skip-gtids -v mysql-bin.000006 > 6.sql
mysqlbinlog –skip-gtids -v mysql-bin.000007 > 7.sql
数据未恢复前
执行恢复命令如下
FLUSH LOGS;\
INSERT INTO t2 SELECT * FROM t;\
DELETE FROM t;\
INSERT INTO t SELECT * FROM t1;
数据恢复后
上面的binlog我们是从第5个binlog开始备份的。下面从第二个binlog开始执行恢复。 会多出了一条记录
原因在于:mysql-bin.000004里面删除delete from t where b=’A’;包
括两条记录(a=1,a=2),在一个事务里面。要么成功,要么失败。
而从mysql-bin.000002开始执行的时候,由于表里面没有a=2的记录,
所以a=2插入成功,但是删除的时候,a=1没有,事务不成功。导致多了一条记录
答案:从基于备份时间点之前,应用binlog来恢复,数据是不一致的。但是如果从第一个binlog起, 执行多次不会有问题。
canal更新zk offset的频率控制是什么样的。
这个问题显然是一个伪命题且属于一个平行问题,关于canal更新zk信息的解释如下:
-
对于平行问题一
基于zk的canal部署显然是高可用的,这里的高可用。不是针对server级别,而是针对采集实例(instance)级别的高可用.假设现在有这样一个场景:
canal_A1和canal_A2为一组,
conf目录下配置instance1,instance2,instance3,instance4
canal_B1和canal_B2为一组,
conf目录下配置instance5,instance6,instance7,instance8
相应的消费端同理,一组只消费1,2,3,4,另一组只消费5,6,7,8
zk保存的数据,即为instace节点的状态
可以连上zk,手动在/otter/canal/destinations
目录下找到canal的所有实例,
实例下有running节点,用set命令改active的值就行了
- 对于平行问题二 offset的频率控制是什么样的。 这里我们一般说的是binlog文件当前写入的pos为止 加本次写入数据的偏移量(offset),以下参数可以配置
#canal 持久化数据到zk上的更新频率,单位毫秒
canal.zookeeper.flush.period = 1000
#canal持久化数据到file上的目录,默认和instance.properties为同一目录
canal.file.data.dir = ${canal.conf.dir}
#canal持久化数据到file上的更新频率,单位毫秒
canal.file.flush.period = 1000
- Q&A 问题4
canal的高可用部署
canal的数据传输有两块,一块是进行binlog订阅时, binlog转换为我们所定义的Message, 第二块是client与server进行TCP交互时,传输的TCP协议。
对于第一块而言是在mysql订阅instance消息的时候进行传输 对于第二块而言是在client与canalserver上的instance交互的时候进行传输
1. 启动两个监听example1的canal client,
启动两个监听example2的canal client:
在example1或example2对应的数据发生变化时,
两个canal client只有一个消费消息。
当两个监听同一个队列的canal client有一个宕掉时,
再有数据变化时,剩下的一个canal client就会开始消费数据。
这就验证了canal client的HA机制:为了保证有序性,一
份instance同一时间只能由一个canal client进行get/ack/rollback
操作,否则客户端接收无法保证有序.
2. 启动两个canal server并在zk上注册
当停掉其中一个canal server时,当产生数据变化时,整个canal server集群仍可以正常对外提供服务。
这就验证了canal server的HA机制:为了减少对mysql dump的请求,不同server上的instance要求同一时间只能有一个处于running,其他的处于standby状态.
3. 在canal server切换过程中,canal client存在重复消费数据的问题
这点需要在消费端自行进行处理
2)分发侧基于什么实现 数据分发过程
- 广播流如何使用
用来获取动态规则实时推送规则
用法:
public class EtlBroadcastProcessFunction extends BroadcastProcessFunction<String,Map<String, JobConfig>,Tuple2<Integer, Map<String, Object>>> {
//解析规则
private JobConfig jobConfig;
/**
* 处理数据流
* @param record
* @param ctx
* @param out
* @throws Exception
*/
@Override
public void processElement(String record, ReadOnlyContext ctx, Collector<Tuple2<Integer, Map<String, Object>>> out) throws Exception {
//record为要处理的数据,可利用获取到的规则jobConfig来检测收到的数据
}
/**
* 获取规则流并缓存
* @param value
* @param ctx
* @param out
* @throws Exception
*/
@Override
public void processBroadcastElement(Map<String, JobConfig> value, Context ctx, Collector<Tuple2<Integer, Map<String, Object>>> out) throws Exception {
//value中为获取到的规则数据
//缓存规则数据
this.jobConfig = value.get(“jobConfig“);
}
}
原理:
-
用户必须创建一个
MapStateDescriptor,才能得到对应的状态句柄。 这保存了状态名称, 状态所持有值的类型,并且可能包含用户指定的函数 -
checkpoint的时候也会checkpoint broadcast state
-
Broadcast State只在内存有,没有RocksDB state backend
-
Flink 会将state广播到每个task,注意该state并不会跨task传播,对其修改仅仅是作用在其所在的task
-
下游tasks接收到broadcast event的顺序可能不一样,所以依赖其到达顺序来处理element的时候要小心
-
flink checkpoint参数如何配置
Flink 的 Checkpoint 包括如下几个部分:
- JM trigger checkpoint
- Source 收到 trigger checkpoint 的 PRC,自己开始做 snapshot,并往下游发送 barrier
- 下游接收 barrier(需要 barrier 都到齐才会开始做 checkpoint)
- Task 开始同步阶段 snapshot
- Task 开始异步阶段 snapshot
- Task snapshot 完成,汇报给 JM
checkpointDir=hdfs://ns3/flink-checkpoints
enableCheckPointing=30000
enableExternalizedCheckPoints=RETAIN_ON_CANCELLATION
failOnCheckPointingErrors=false
maxConcurrentCheckpoint=1
minPauseBetweenCheckpoints=30000
parallelism=2
useSnapshotCompression=false
- flink线上环境算子链并发度如何分配?
沙盒环境当前数据看:主要瓶颈点在join 算子, 处理数据的qps 和 并发数成正比。 sink doris 并发数在16时,处理数据的qps 11w,提高并发到20, 处理速度仍为11w.并行度 大于 kafka 的partition 数时,吞吐不能提高的, 多的task ,分配不到数据。
线上环境
- 1、source 30并发, join、flatemap 50并发 、 sink 10并发
整体处理qps :20w 。 单个并发需要的内存8g
- 2、source 30并发, join、flatemap 50并发 、 sink 50并发
整体处理qps :40w 。 单个并发需要的内存2g
- 3、source 30并发, join、flatemap 60并发 、 sink 60并发
整体处理qps :47w 。 单个并发需要的内存2g
- 4、source 30并发, join、flatemap 60并发 、 去除sink算子
整体处理qps :60w 。 单个并发需要的内存2g
2)如何保证到达的数据不乱序
因为我们的系统里 绝对不允许因为数据延迟到达,而造成乱序的情况
所以我们将上游从mysql数据库采集到的数据存到中继kafka, 每个topic作为库级别的数据存储
然后定制化一个入库平台,用户配置各种分发规则后
-
流处理这一侧 库级别topic的每个分区的数据,分发到下游表级别的topic
-
批处理的这一侧 库级别的topic按照小时级别入cosn。由于我们在写入cosn的时候 写入超时,引发了如下问题: juejin.cn/post/697791…
3.kafka在入库分发的时候性能怎么样?
kafka 50台 flink190台
Kafka集群的网络没做好,产生了竞争, 导致Kafka的Producer发送慢了,然后发不出去的消息一直堆在Producer缓冲区, 最后放不下了报错,因为是EXACTLY_ONCE, 并且Flink Producer没有设置logFailuresOnly=true,所以任务会挂掉
4.flink内存怎么计算分配
参考:cloud.tencent.com/developer/a…
- Flink Memory = Totoal Memory - JVM Metaspace - JVM Overhead
2)Total Memory :flink job 启动时,按照参数: -ytm 6144 指定的内存大小(单位M)。如果不指定,按照yarn最小Container分配的内存大小。
yarn.scheduler.minimum-allocation-mb 20483)Network = Flink Memory *0.1(默认值)
4)Managed Memory = Flink Memory * 0.4(默认值)
5)Task off-heap 默认为0;
6)framework off-heap :默认为128M
5.flink的solt怎么分配?
taskmangaer数量 = 全局并行度/solt数量
根据算子的并行度算需要多少个taskmanager 哪些算子使用soltsharegroup作为一个单独的算子链 具体情况具体分析
假设并行度为1000,
方式一:设置100个TM,每个TM设置10个slot,每个设置5g内存,5core;
方式二:设置200个TM,每个TM设置5个slot,每个设置2.5g内存,2.5core(忽略小数的问题)。
先说答案:没有绝对的好坏,更分散会增加TM 之间数据交换开销, 更集中的话,如果对状态访问较多,会导致对磁盘压力太大
缺点:状态访问较多,会导致对磁盘压力太大 打个比方: 如果只有50台机器的话,那么每台机器上使用方式一的2个TM或者方式二的4个TM 对磁盘来说压力是一样的,但实际不会这么理想 在集群有很多任务的情况下,假设100个TM大概会分布在10台机器上 这个时候对单机的磁盘压力就体现出来了,如果磁盘资源确实紧张 就考虑平衡taskmanager个数与solt个数/tm的关系
一般设置内存:0.5gslot个数/TM,core:0.5slot个数/TM
Continue..QwQ待更新