离线数仓—增量表同步实现
前言
在确定了全量表使用DataX进行同步,增量表使用Maxwell进行同步,且学习了Maxwell相关使用方法之后,开始着手进行增量表数据同步的实现。
一、增量表数据同步
1.增量表数据通道
增量表的数据由Maxwell从MySQL数据库导出,经过Kafka、Flume最终导入到HDFS中

Maxwell实时监控MySQL数据库中的增量表的变化,将发生变化的表的变更数据(JSON类型)发送到Kafka中对应表名的topic中,然后Flume将Kafka中一个topic的数发送到HDFS里。
Maxwell是不支持直接往HDFS里发送数据的,而在大数据领域内,最常用而且支持的是Kafka。
二、MySQL—Maxwell—Kafka的实现
1.Maxwell配置
先回顾一下,所有的增量表如下图所示:

按照规划,总共有13张表需要进行增量同步,因此对Maxwell进行相应的配置,只同步这13张表。
同时,为了方便起见,需要对Maxwell进行额外的配置,把对应表的变更数据发送到对应的topic中。
配置过程如下:
1)修改Maxwell配置文件config.proties:vim /opt/module/maxwell/config.properties,修改后参数配置如下:
log_level=info
producer=kafka
kafka.bootstrap.servers=hadoop102:9092,hadoop103:9092
#kafka topic动态配置
kafka_topic=%{table}
# mysql login info
host=hadoop102
user=maxwell
password=maxwell
jdbc_options=useSSL=false&serverTimezone=Asia/Shanghai
#表过滤,只同步特定的13张表
filter= include:gmall.cart_info,include:gmall.comment_info,include:gmall.coupon_use,include:gmall.favor_info,include:gmall.order_detail,include:gmall.order_detail_activity,include:gmall.order_detail_coupon,include:gmall.order_info,include:gmall.order_refund_info,include:gmall.order_status_log,include:gmall.payment_info,include:gmall.refund_payment,include:gmall.user_info
参数说明:
kafka.bootstrap.servers:尽量写多个地址,防止其中一个地址连接不上而出错
kafka_topic:这个参数是写入到的kafka的topic名称,可以是固定的也可以是动态的,例如 maxwell_%{database}_%{table} 这种写法会自动获取对应的database和table名称
filter:这是为了对表进行过滤,Maxwell只能监听某个数据库,不能指定监听某个数据库中的哪些表,这里过滤出来需要的表的名称
2)重启Maxwell
2.Maxwell—Kafka通道测试
在1中完成了Maxwell的配置,接着可以对整个增量表同步通道进行测试。
1)启动zookeeper和kafka集群
2)启动一个kafka consumer,消费任意一个上述的topic数据
3)生成模拟数据
4)观察Kafka是否能够消费到数据
三、Kafka—Flume—HDFS实现
1.Flume配置选取
这里是将Kafka的数据传输到HDFS中,因此Source选取Kafka Source,Sink选取HDFS Sink,Channel选取File Channel。
2.Flume配置关键点

因为Kafka中的数据是存放在13个topic中,因此Kafka Source需要订阅13个topic,然后将对应topic放到HDFS对应的路径上。
一个具体的实例如下:

3.Flume配置具体实现
1)创建Flume配置文件:vim job/kafka_to_hdfs_db.conf
2)配置文件内容如下:
#为各组件命名
a1.sources = r1
a1.channels = c1
a1.sinks = s1
tier1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
tier1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
tier1.sources.r1.kafka.topics = cart_info,comment_info,coupon_use,favor_info,order_detail_activity,order_detail_coupon,order_detail,order_info,order_refund_info,order_status_log,payment_info,refund_payment,user_info
a1.sources.r1.kafka.consumer.group.id = flume
a1.sources.r1.setTopicHeader = true
a1.sources.r1.topicHeader = topic
a1.sources.interceptors=i1
a1.sources.interceptors.i1.type=com.atguigu.flume.interceptor.db.TimestampInterceptor$Builder
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
a1.channels.c1.maxFileSize = 2146435071
a1.channels.c1.capacity = 1000000
a1.channels.c1.keep-alive = 6
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/db/${topic}_inc/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = db
a1.sinks.k1.hdfs.round = false
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = gzip
a1.sources1.channels=c1
a1.sinks.k1.channel=c1
3)编写Flume拦截器
先导入Maven依赖:
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
编写TimestampInterceptor类:
public class TimeStampInterceptor implements Interceptor {
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
byte[] body=event.getBody();
String log=new String(body, StandardCharsets.UTF_8);
JSONObject jsonObject=JSONObject.parseObject(log);
Long ts=jsonObject.getLong("ts");
Map<String,String> headers= event.getHeaders();
headers.put("timestamp",ts+"000");
return event;
}
@Override
public List<Event> intercept(List<Event> list) {
for(Event event:list){
intercept(event);
}
return list;
}
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder {
@Override
public Interceptor build() {
return new TimeStampInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
4)最后,打包,将生成的包上传到Flume的lib文件夹下
4.通道测试
在3中完成了Flume配置文件的编写,接下来就可以使用了。
1)启动zookeeper、Kafka集群
2)启动hadoop104的Flume(在日志中hadoop104就作为消费者将日志数据从Kafka传输到HDFS,这里也使用hadoop104)bin/flume-ng agent -n a1 -c conf/ -f job/kafka_to_hdfs_db.conf -Dflume.root.logger=info,console
3)生成模拟数据
4)观察HDFS上的目标路径是否有数据出现,若有数据出现,则通道已经打通
5.说明
目标路径中的日期是Maxwell中输出的JSON字符串中的ts字段的值,如果采用模拟生成之前日期的数据,因为生成数据的时间是今天,而生成的数据是以前时间的数据,所以最后放到HDFS里的路径日期就是数据变动的时间也就是今天,这样数据会发生混乱。
四、优化总结
1.增量表同步Flume启停脚本
为了方便使用,编写一个增量表同步的Flume启停脚本,脚本内容如下:
#!/bin/bash
case $1 in
"start")
echo " --------启动 hadoop104 业务数据flume-------"
ssh hadoop104 "nohup /opt/module/flume/bin/flume-ng agent -n a1 -c /opt/module/flume/conf -f /opt/module/flume/job/kafka_to_hdfs_db.conf >/dev/null 2>&1 &"
;;
"stop")
echo " --------停止 hadoop104 业务数据flume-------"
ssh hadoop104 "ps -ef | grep kafka_to_hdfs_db.conf | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
;;
esac
2.增量表首日同步
前面完成的是增量表的实时同步,但是一般情况下,数据库中原来是存在一定的数据的,因此在增量同步前,首日先对数据库进行一个全量同步。
这里增量表首日全量同步采用的是Maxwell而不是DataX,原因是:DataX同步到的数据格式就是一行一行的数据,而Maxwell增量同步拿到的数据是JSON格式的数据,数据类型不一致,不方便进行处理,所以为了保持数据格式一致,方便处理,采用Maxwell进行首日的全量同步。
增量表首日全量同步拿到的数据也是JSON类型的数据,和每日同步的区别在于type类型不同,因此首日全量同步也需要启动Kafka、Flume。
增量表首日同步脚本内容如下:
#!/bin/bash
# 该脚本的作用是初始化所有的增量表,只需执行一次
MAXWELL_HOME=/opt/module/maxwell
import_data() {
$MAXWELL_HOME/bin/maxwell-bootstrap --database gmall --table $1 --config $MAXWELL_HOME/config.properties
}
case $1 in
"cart_info")
import_data cart_info
;;
"comment_info")
import_data comment_info
;;
"coupon_use")
import_data coupon_use
;;
"favor_info")
import_data favor_info
;;
"order_detail")
import_data order_detail
;;
"order_detail_activity")
import_data order_detail_activity
;;
"order_detail_coupon")
import_data order_detail_coupon
;;
"order_info")
import_data order_info
;;
"order_refund_info")
import_data order_refund_info
;;
"order_status_log")
import_data order_status_log
;;
"payment_info")
import_data payment_info
;;
"refund_payment")
import_data refund_payment
;;
"user_info")
import_data user_info
;;
"all")
import_data cart_info
import_data comment_info
import_data coupon_use
import_data favor_info
import_data order_detail
import_data order_detail_activity
import_data order_detail_coupon
import_data order_info
import_data order_refund_info
import_data order_status_log
import_data payment_info
import_data refund_payment
import_data user_info
;;
esac
这首日的全量同步脚本在整个项目中只需要在首日执行一次即可(在首日全量同步前,要确保里面数据为空)。
3.增量表同步总结
增量表同步分为两个部分,首日的全量同步和每天的增量同步。首日进行全量同步时,要先确保所有增量表文件夹内容为空,要启动Maxwell、Kafka、Flume,然后执行增量表首日同步脚本。之后的每日增量同步只需要保证整个采集通道正常运行即可。

本文详细介绍了如何通过Maxwell监控MySQL增量表,将变化数据经Kafka发送到Flume,最终存入HDFS的过程。配置包括Maxwell的表过滤、Kafka Topic设置,以及Flume的Source、Sink和Channel选择。测试和优化环节确保了数据准确无误地传输到HDFS,并提供了增量同步的总结和脚本。
2275

被折叠的 条评论
为什么被折叠?



