【大数据】数仓5.0_业务采集➕数据同步策略(数仓环境搭建完成)


前言

紧接上一篇👉点击前往数仓准备工作


一、日志采集flume in kafka

按照规划,需要采集的用户行为日志文件分布在hadoop102,hadoop103两台日志服务器,故需要在hadoop102,hadoop103两台节点配置日志采集Flume。日志采集Flume需要采集日志文件内容,并对日志格式(JSON)进行校验,然后将校验通过的日志发送到Kafka

🔨此处可选择TaildirSourceKafkaChannel,并配置日志校验拦截器。选择TailDirSource和KafkaChannel的原因如下:

  1. TailDirSource
    TailDirSource相比ExecSource、SpoolingDirectorySource的优势
    TailDirSource:断点续传、多目录。Flume1.6以前需要自己自定义Source记录每次读取文件位置,实现断点续传。
    ExecSource可以实时搜集数据,但是在Flume不运行或者Shell命令出错的情况下,数据将会丢失。
    SpoolingDirectorySource监控目录,支持断点续传。
  2. KafkaChannel
    采用Kafka Channel,省去了Sink,提高了效率。

🔨日志采集Flume关键配置如下:
在这里插入图片描述

Kafka channel
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
经过分析可知:第二种更适合(不要sinks)

1. 配置flume

🔨配置文件如下:
在这里插入图片描述

# 创建文件
cd /opt/module/flume-1.9.0;touch /opt/module/flume-1.9.0/taildir_position.json;vim job/file_to_kafka.conf

# =====================添加如下内容========================
#定义组件
a1.sources = r1
a1.channels = c1

#配置source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/module/applog/log/app.*
a1.sources.r1.positionFile = /opt/module/flume-1.9.0/taildir_position.json

#配置channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.channels.c1.kafka.topic = topic_log
a1.channels.c1.parseAsFlumeEvent = false

#组装 
a1.sources.r1.channels = c1

🔨启动flumekafka测试

# 102flume
cd /opt/module/flume-1.9.0
bin/flume-ng agent -n a1 -c conf/ -f job/file_to_kafka.conf -Dflume.root.logger=info,console

# 103上启动一个Kafka的Console-Consumer
kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic topic_log

# 生成一下数据
lg.sh

在这里插入图片描述

🔨进行一个flume的轻度清洗(ETL),加个拦截器,先打开IDEA,创建一个maven项目
在这里插入图片描述

  • pom.xml文件中添加如下配置:
<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>
  • com.ygy.gmall.flume.utils包(自己先创建好)下创建JSONUtil
package com.ygy.gmall.flume.utils;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONException;

public class JSONUtil {
    /*
     * 通过异常判断是否是json字符串
     * 是:返回true  不是:返回false
     * */
    public static boolean isJSONValidate(String log){
        try {
            JSONObject.parseObject(log);
            return true;
        }catch (JSONException e){
            return false;
        }
    }
}
  • com.atguigu.gmall.flume.interceptor包下创建ETLInterceptor
package com.ygy.gmall.flume.interceptor;
import com.ygy.gmall.flume.utils.JSONUtil;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;

public class ETLInterceptor implements Interceptor {

    @Override
    public void initialize() {}
    @Override
    public Event intercept(Event event) {

        //1、获取body当中的数据并转成字符串
        byte[] body = event.getBody();
        String log = new String(body, StandardCharsets.UTF_8);
        //2、判断字符串是否是一个合法的json,是:返7回当前event;不是:返回null
        if (JSONUtil.isJSONValidate(log)) {
            return event;
        } else {
            return null;
        }
    }

    @Override
    public List<Event> intercept(List<Event> list) {
        Iterator<Event> iterator = list.iterator();
        while (iterator.hasNext()){
            Event next = iterator.next();
            if(intercept(next)==null){
                iterator.remove();
            }
        }
        return list;
    }

    public static class Builder implements Interceptor.Builder{

        @Override
        public Interceptor build() {
            return new ETLInterceptor();
        }
        @Override
        public void configure(Context context) {
        }
    }
    @Override
    public void close() {
    }
}
  • 打包并打开其在文件中的位置
    在这里插入图片描述
# 导入一气呵成
sftp root@hadoop102
lcd F:\Maven\flume-interceptor\target
cd /opt/module/flume-1.9.0/lib/
put flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar
  • 在配置文件vim /opt/module/flume-1.9.0/job/file_to_kafka.conf中加入:
#配置source(Builder的全类名)
a1.sources.r1.interceptors =  i1
a1.sources.r1.interceptors.i1.type = com.ygy.gmall.flume.interceptor.ETLInterceptor$Builder

2. 日志采集flume测试

如果出现Connection to node -1 (/ip:9092) could not be established. Broker may not be available.报错,检查一下kafka开了没

  • 再次启动flumekafka测试一下
# 102flume
cd /opt/module/flume-1.9.0
bin/flume-ng agent -n a1 -c conf/ -f job/file_to_kafka.conf -Dflume.root.logger=info,console

# 103上启动一个Kafka的Console-Consumer
kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic topic_log

# 给数据搞点东西,一条对一条错的
lg.sh
cd /opt/module/applog/
echo '{id:1}' >> app.2023-10-05.log
echo '{id:' >> app.2023-10-05.log

在这里插入图片描述

分发flumecd /opt/module;xsync flume-1.9.0
在这里插入图片描述

3. 日志采集Flume启停脚本(针对file_to_kafka.conf)

vim /root/bin/f1.sh

#!/bin/bash
case $1 in
"start"){
        for i in hadoop102 hadoop103
        do
                echo " --------启动 $i 采集flume-------"
                ssh $i "nohup /opt/module/flume-1.9.0/bin/flume-ng agent -n a1 -c /opt/module/flume-1.9.0/conf/ -f /opt/module/flume-1.9.0/job/file_to_kafka.conf >/dev/null 2>&1 &"
        done
};; 
"stop"){
        for i in hadoop102 hadoop103
        do
                echo " --------停止 $i 采集flume-------"
                ssh $i "ps -ef | grep file_to_kafka | grep -v grep |awk  '{print \$2}' | xargs -n1 kill -9 "
        done

};;
esac

👉chmod 777 f1.sh加权限

# 测试
jpsall
f1.sh start;jpsall
f1.sh stop;jpsall

二、业务数据采集平台

1. 电商业务简介

1.1. 电商业务流程

🔨电商的业务流程可以以一个普通用户的浏览足迹为例进行说明,用户点开电商首页开始浏览,可能会通过分类查询也可能通过全文搜索寻找自己中意的商品,这些商品都是存储在后台的管理系统中

当用户寻找到自己中意的商品,可能会想要购买,将商品添加到购物车后发现需要登录,登录后对商品进行结算,这时候购物车的管理和商品订单信息的生成都会对业务数据库产生影响,会生成相应的订单数据和支付数据。订单正式生成之后,还会对订单进行跟踪处理,直到订单全部完成。

🔨电商的主要业务流程包括用户前台浏览商品时的商品详情的管理,用户商品加入购物车进行支付时用户个人中心&支付服务的管理,用户支付完成后订单后台服务的管理,这些流程涉及到了十几个甚至几十个业务数据表,甚至更多
在这里插入图片描述

1.2. 电商常识

🔨SKU和SPU

  • SKU = Stock Keeping Unit(库存量基本单位)。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。
  • SPU(Standard Product Unit):是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息集合。
  • 例如:iPhoneX手机就是SPU。一台银色、128G内存的、支持联通网络的iPhoneX,就是SKU。

在这里插入图片描述

SPU表示一类商品。同一SPU的商品可以共用商品图片、海报、销售属性等

  • 平台属性
    在这里插入图片描述
  • 销售属性
    在这里插入图片描述

2. 业务数据介绍

2.1. 电商系统表结构

🔨以下为本电商数仓系统涉及到的业务数据表结构关系。这34个表以订单表、用户表、SKU商品表、活动表和优惠券表为中心,延伸出了优惠券领用表、支付流水表、活动订单表、订单详情表、订单状态表、商品评论表、编码字典表退单表、SPU商品表等,用户表提供用户的详细信息,支付流水表提供该订单的支付详情,订单详情表提供订单的商品数量等情况,商品表给订单详情表提供商品的详细信息。本次讲解以此34个表为例,实际项目中,业务数据库中表格远远不止这些
在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.2. MySQL安装

👉点击查看MySQL安装教程

# 查看MySQL默认初始密码
cat /var/log/mysqld.log | grep password
# 查询user表
select user,host from user;
# 修改user表,把Host表内容修改为%,使得任意节点均可访问
update user set host="%" where user="root";
# 刷新权限
select user,host from user;

在这里插入图片描述

最后用navicat或者DG连接数据库

2.3. 业务数据模拟

🔨通过MySQL可视化客户端连接数据库

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

🔨生成业务数据

# 在hadoop102的/opt/module/目录下创建db_log文件夹
cd /opt/module;mkdir db_log/;ll

# 把gmall2020-mock-db-2021-11-14.jar和application.properties上传到hadoop102的/opt/module/db_log路径上(再开一个cmd)
sftp root@hadoop102
lcd G:\大数据
cd /opt/module/db_log
put gmall.zip
  • 根据需求修改application.properties相关配置
# 密码记得改成自己的密码
spring.datasource.password=000000


#是否重置(1是覆盖,0是追加)
mock.clear=1
#是否重置用户 注意:第一次执行必须设置为1,后续不需要重置不用设置为1(1是覆盖,0是追加)
mock.clear.user=1
  • 最后向表中导入数据
# 解压执行jar包
cd db_log;unzip gmall.zip
java -jar gmall2020-mock-db-2021-11-14.jar

查看gmall数据库,观察是否有2020-06-14的数据出现

3. 业务数据采集模块

🔨数据通道
在这里插入图片描述

🔨采集工具:👉点击前往Maxwell安装使用

# 修改Maxwell配置文件config.properties
vim /opt/module/maxwell/config.properties

#kafka topic配置
kafka_topic=topic_db

# 重新启动Maxwell
mxw.sh restart

🌰测试一下,启动Zookeeper以及Kafka集群

# 启动一个Kafka Console Consumer,消费topic_db数据
kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic topic_db

# 生成模拟数据
cd /opt/module/db_log/;java -jar gmall2020-mock-db-2021-11-14.jar

在这里插入图片描述

三、离线数仓数据同步策略

实时数仓同步数据:实时数仓由Flink源源不断从Kafka当中读数据计算,所以不需要手动同步数据到实时数仓
在这里插入图片描述

1. 用户行为数据同步

数据通道:用户行为数据由Flume从Kafka直接同步到HDFS,由于离线数仓采用Hive的分区表按天统计,所以目标路径要包含一层日期。具体数据流向如下图所示
在这里插入图片描述

1.1. 日志消费flume配置

🔨日志消费Flume配置概述:按照规划,该Flume需将Kafka中topic_log的数据发往HDFS。并且对每天产生的用户行为日志进行区分(加一个带有日期的header),将不同天的数据发往HDFS不同天的路径,因而选择KafkaSourceFileChannelHDFSSink。关键配置如下:
在这里插入图片描述

🔨在hadoop104节点的Flume的job目录下创建vim /opt/module/flume-1.9.0/job/kafka_to_hdfs_log.conf

#定义组件
a1.sources=r1
a1.channels=c1
a1.sinks=k1

#配置source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.ygy.gmall.flume.interceptor.TimestampInterceptor$Builder

#配置channel
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume-1.9.0/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume-1.9.0/data/behavior1
a1.channels.c1.maxFileSize = 2146435071
a1.channels.c1.capacity = 1000000
a1.channels.c1.keep-alive = 6

#配置sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = log
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.sources.r1.channels = c1
a1.sinks.k1.channel = c1

配置优化

  1. FileChannel优化
    通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。
    官方说明如下:Comma separated list of directories for storing log files. Using multiple directories on separate disks can improve file channel peformance
    👉checkpointDir和backupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据
  2. HDFS Sink优化
    (1)HDFS存入大量小文件,有什么影响?
    元数据层面:每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等,这些信息都保存在Namenode内存中。所以小文件过多,会占用Namenode服务器大量内存,影响Namenode性能和使用寿命
    计算层面:默认情况下MR会对每个小文件启用一个Map任务计算,非常影响计算性能。同时也影响磁盘寻址时间。
    (2)HDFS小文件处理
    官方默认的这三个参数配置写入HDFS后会产生小文件,hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount
    基于以上hdfs.rollInterval=3600,hdfs.rollSize=134217728,hdfs.rollCount =0几个参数综合作用,效果如下:
    (1)文件在达到128M时会滚动生成新文件
    (2)文件创建超3600秒时会滚动生成新文件

🔨编写Flume拦截器(数据漂移问题)
在这里插入图片描述

  • 在上次编写拦截器的项目中com.ygy.gmall.flume.interceptor包下创建TimestampInterceptor
package com.ygy.gmall.flume.interceptor;

import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public class TimestampInterceptor implements Interceptor {


    @Override
    public void initialize() {
    }

    @Override
    public Event intercept(Event event) {

        //1、获取header和body的数据
        Map<String, String> headers = event.getHeaders();
        String log = new String(event.getBody(), StandardCharsets.UTF_8);

        //2、将body的数据类型转成jsonObject类型(方便获取数据)
        JSONObject jsonObject = JSONObject.parseObject(log);

        //3、header中timestamp时间字段替换成日志生成的时间戳(解决数据漂移问题)
        String ts = jsonObject.getString("ts");
        headers.put("timestamp", ts);
        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) {
        }
    }
}
  • 104上删除原来的jar包重新打包并上传在这里插入图片描述
# 删除jar包
rm -rf /opt/module/flume-1.9.0/lib/flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar
# 重新上传
sftp root@hadoop104
cd /opt/module/flume-1.9.0/lib
lcd F:\Maven\flume-interceptor\target
put flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar 

🌰测试一下:

# 102启动z/k/f
zk.sh start
kf.sh start
f1.sh start
# 104上从Kafka采集到hdfs
cd /opt/module/flume-1.9.0;bin/flume-ng agent -n a1 -c conf/ -f job/kafka_to_hdfs_log.conf -Dflume.root.logger=info,console
# 生成模拟数据
lg.sh

👉观察HDFS是否出现数据
在这里插入图片描述

1.2. 日志消费Flume启停脚本

vim /root/bin/f2.sh

#!/bin/bash
case $1 in
"start")
        echo " --------启动 hadoop104 日志数据flume-------"
        ssh hadoop104 "nohup /opt/module/flume-1.9.0/bin/flume-ng agent -n a1 -c /opt/module/flume-1.9.0/conf -f /opt/module/flume-1.9.0/job/kafka_to_hdfs_log.conf >/dev/null 2>&1 &"
;;
"stop")

        echo " --------停止 hadoop104 日志数据flume-------"
        ssh hadoop104 "ps -ef | grep kafka_to_hdfs_log | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
;;
esac

👉最后chmod 777 /root/bin/f2.sh

2. 业务数据同步

2.1. 同步策略简述

🔨数据同步策略概述:业务数据是数据仓库的重要数据来源,我们需要每日定时从业务数据库中抽取数据,传输到数据仓库中,之后再对数据进行分析统计。为保证统计结果的正确性,需要保证数据仓库中的数据与业务数据库是同步的,离线数仓的计算周期通常为天,所以数据同步周期也通常为天,即每天同步一次即可

🔨数据的同步策略有全量同步和增量同步

  • 全量同步,就是每天都将业务数据库中的全部数据同步一份到数据仓库,这是保证两侧数据同步的最简单的方式在这里插入图片描述
  • 增量同步,就是每天只将业务数据中的新增及变化数据同步到数据仓库。采用每日增量同步的表,通常需要在首日先进行一次全量同步在这里插入图片描述
同步策略优点缺点
全量同步逻辑简单在某些情况下效率较低。例如某张表数据量较大,但是每天数据的变化比例很低,若对其采用每日全量同步,则会重复同步和存储大量相同的数据。
增量同步效率高,无需同步和存储重复数据逻辑复杂,需要将每日的新增及变化数据同原来的数据进行整合,才能使用

通常情况,业务表数据量比较大,优先考虑增量,数据量比较小,优先考虑全量;具体选择由数仓模型决定在这里插入图片描述
在这里插入图片描述

3. 数据同步工具

3.1. 数据同步简介

🔨数据同步工具种类繁多,大致可分为两类

  • 以DataX、Sqoop为代表的基于Select查询的离线、批量同步工具
  • 以Maxwell、Canal为代表的基于数据库数据变更日志(例如MySQL的binlog,其会实时记录所有的insert、update以及delete操作)的实时流式同步工具。

🔨全量同步通常使用DataX、Sqoop等基于Select查询的离线同步工具。而增量同步既可以使用DataX、Sqoop等工具,也可使用Maxwell、Canal等工具,下面对增量同步不同方案进行简要对比

增量同步方案DataX/SqoopMaxwell/Canal
对数据库的要求原理是基于查询,故若想通过select查询获取新增及变化数据,就要求数据表中存在create_time、update_time等字段,然后根据这些字段获取变更数据。要求数据库记录变更操作,例如MySQL需开启binlog。
数据的中间状态由于是离线批量同步,故若一条数据在一天中变化多次,该方案只能获取最后一个状态,中间状态无法获取。由于是实时获取所有的数据变更操作,所以可以获取变更数据的所有中间状态。

3.2. 全量表数据同步(DataX:MySQL=>HDFS)

👉点击前往DataX安装

🔨数据通道:全量表数据由DataX从MySQL业务数据库直接同步到HDFS,具体数据流向如下图所示
在这里插入图片描述

如果表的数量增多,要写的json文件也会增多,故尝试写一个模板再写一个脚本一键生成,因为都是mysqlreader和hdfswriter,所有配置一个json模板动态生成json文件

🔨DataX配置文件
我们需要为每张全量表编写一个DataX的json配置文件,此处以activity_info为例,配置文件内容如下

{
    "job": {
        "content": [
            {
                "reader": {
                    "name": "mysqlreader",
                    "parameter": {
                        "column": [
                            "id",
                            "activity_name",
                            "activity_type",
                            "activity_desc",
                            "start_time",
                            "end_time",
                            "create_time"
                        ],
                        "connection": [
                            {
                                "jdbcUrl": [
                                    "jdbc:mysql://hadoop102:3306/gmall"
                                ],
                                "table": [
                                    "activity_info"
                                ]
                            }
                        ],
                        "password": "000000",
                        "splitPk": "",
                        "username": "root"
                    }
                },
                "writer": {
                    "name": "hdfswriter",
                    "parameter": {
                        "column": [
                            {
                                "name": "id",
                                "type": "bigint"
                            },
                            {
                                "name": "activity_name",
                                "type": "string"
                            },
                            {
                                "name": "activity_type",
                                "type": "string"
                            },
                            {
                                "name": "activity_desc",
                                "type": "string"
                            },
                            {
                                "name": "start_time",
                                "type": "string"
                            },
                            {
                                "name": "end_time",
                                "type": "string"
                            },
                            {
                                "name": "create_time",
                                "type": "string"
                            }
                        ],
                        "compress": "gzip",
                        "defaultFS": "hdfs://hadoop102:8020",
                        "fieldDelimiter": "\t",
                        "fileName": "activity_info",
                        "fileType": "text",
                        "path": "${targetdir}",
                        "writeMode": "append"
                    }
                }
            }
        ],
        "setting": {
            "speed": {
                "channel": 1
            }
        }
    }
}

注:由于目标路径包含一层日期,用于对不同天的数据加以区分,故path参数并未写死,需在提交任务时通过参数动态传入,参数名称为targetdir

🔨 DataX配置文件生成脚本vim ~/bin/gen_import_config.py

# ecoding=utf-8
import json
import getopt
import os
import sys
import MySQLdb

#MySQL相关配置,需根据实际情况作出修改
mysql_host = "hadoop102"
mysql_port = "3306"
mysql_user = "root"
mysql_passwd = "lovexw999"

#HDFS NameNode相关配置,需根据实际情况作出修改
hdfs_nn_host = "hadoop102"
hdfs_nn_port = "8020"

#生成配置文件的目标路径,可根据实际情况作出修改
output_path = "/opt/module/datax/job/import"


def get_connection():
    return MySQLdb.connect(host=mysql_host, port=int(mysql_port), user=mysql_user, passwd=mysql_passwd)


def get_mysql_meta(database, table):
    connection = get_connection()
    cursor = connection.cursor()
    sql = "SELECT COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS WHERE TABLE_SCHEMA=%s AND TABLE_NAME=%s ORDER BY ORDINAL_POSITION"
    cursor.execute(sql, [database, table])
    fetchall = cursor.fetchall()
    cursor.close()
    connection.close()
    return fetchall


def get_mysql_columns(database, table):
    return map(lambda x: x[0], get_mysql_meta(database, table))


def get_hive_columns(database, table):
    def type_mapping(mysql_type):
        mappings = {
            "bigint": "bigint",
            "int": "bigint",
            "smallint": "bigint",
            "tinyint": "bigint",
            "decimal": "string",
            "double": "double",
            "float": "float",
            "binary": "string",
            "char": "string",
            "varchar": "string",
            "datetime": "string",
            "time": "string",
            "timestamp": "string",
            "date": "string",
            "text": "string"
        }
        return mappings[mysql_type]

    meta = get_mysql_meta(database, table)
    return map(lambda x: {"name": x[0], "type": type_mapping(x[1].lower())}, meta)


def generate_json(source_database, source_table):
    job = {
        "job": {
            "setting": {
                "speed": {
                    "channel": 3
                },
                "errorLimit": {
                    "record": 0,
                    "percentage": 0.02
                }
            },
            "content": [{
                "reader": {
                    "name": "mysqlreader",
                    "parameter": {
                        "username": mysql_user,
                        "password": mysql_passwd,
                        "column": get_mysql_columns(source_database, source_table),
                        "splitPk": "",
                        "connection": [{
                            "table": [source_table],
                            "jdbcUrl": ["jdbc:mysql://" + mysql_host + ":" + mysql_port + "/" + source_database]
                        }]
                    }
                },
                "writer": {
                    "name": "hdfswriter",
                    "parameter": {
                        "defaultFS": "hdfs://" + hdfs_nn_host + ":" + hdfs_nn_port,
                        "fileType": "text",
                        "path": "${targetdir}",
                        "fileName": source_table,
                        "column": get_hive_columns(source_database, source_table),
                        "writeMode": "append",
                        "fieldDelimiter": "\t",
                        "compress": "gzip"
                    }
                }
            }]
        }
    }
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    with open(os.path.join(output_path, ".".join([source_database, source_table, "json"])), "w") as f:
        json.dump(job, f)


def main(args):
    source_database = ""
    source_table = ""

    options, arguments = getopt.getopt(args, '-d:-t:', ['sourcedb=', 'sourcetbl='])
    for opt_name, opt_value in options:
        if opt_name in ('-d', '--sourcedb'):
            source_database = opt_value
        if opt_name in ('-t', '--sourcetbl'):
            source_table = opt_value

    generate_json(source_database, source_table)


if __name__ == '__main__':
    main(sys.argv[1:])
  • 由于需要使用Python访问Mysql数据库,故需安装驱动,命令如下:
yum install -y MySQL-python
# 脚本使用说明
python gen_import_config.py -d database -t table
  • 上面一键写脚本生成好了,我们只需要一条一条执行上面脚本就行了,因而也可以编写一个脚本一条一条执行,在~/bin目录下创建vim ~/bin/gen_import_config.sh脚本
#!/bin/bash
python ~/bin/gen_import_config.py -d gmall -t activity_info
python ~/bin/gen_import_config.py -d gmall -t activity_rule
python ~/bin/gen_import_config.py -d gmall -t base_category1
python ~/bin/gen_import_config.py -d gmall -t base_category2
python ~/bin/gen_import_config.py -d gmall -t base_category3
python ~/bin/gen_import_config.py -d gmall -t base_dic
python ~/bin/gen_import_config.py -d gmall -t base_province
python ~/bin/gen_import_config.py -d gmall -t base_region
python ~/bin/gen_import_config.py -d gmall -t base_trademark
python ~/bin/gen_import_config.py -d gmall -t cart_info
python ~/bin/gen_import_config.py -d gmall -t coupon_info
python ~/bin/gen_import_config.py -d gmall -t sku_attr_value
python ~/bin/gen_import_config.py -d gmall -t sku_info
python ~/bin/gen_import_config.py -d gmall -t sku_sale_attr_value
python ~/bin/gen_import_config.py -d gmall -t spu_info

👉chmod 777 ~/bin/gen_import_config.sh

# 执行该脚本
mkdir /opt/module/datax/job/import/
gen_import_config.sh

如果报错_mysql_exceptions.OperationalError: (2059, "Authentication plugin 'caching_sha2_password' cannot be,👉点击前往blog查看如何修改,然后再次执行直到出现在这里插入图片描述

🔨配置文件准备好了,写一个全量表数据同步脚本vim ~/bin/mysql_to_hdfs_full.sh

#!/bin/bash

DATAX_HOME=/opt/module/datax

# 如果传入日期则do_date等于传入的日期,否则等于前一天日期
if [ -n "$2" ] ;then
    do_date=$2
else
    do_date=`date -d "-1 day" +%F`
fi

#处理目标路径,此处的处理逻辑是,如果目标路径不存在,则创建;若存在,则清空,目的是保证同步任务可重复执行
handle_targetdir() {
  hadoop fs -test -e $1
  if [[ $? -eq 1 ]]; then
    echo "路径$1不存在,正在创建......"
    hadoop fs -mkdir -p $1
  else
    echo "路径$1已经存在"
    fs_count=$(hadoop fs -count $1)
    content_size=$(echo $fs_count | awk '{print $3}')
    if [[ $content_size -eq 0 ]]; then
      echo "路径$1为空"
    else
      echo "路径$1不为空,正在清空......"
      hadoop fs -rm -r -f $1/*
    fi
  fi
}

#数据同步
import_data() {
  datax_config=$1
  target_dir=$2

  handle_targetdir $target_dir
  python $DATAX_HOME/bin/datax.py -p"-Dtargetdir=$target_dir" $datax_config
}

case $1 in
"activity_info")
  import_data /opt/module/datax/job/import/gmall.activity_info.json /origin_data/gmall/db/activity_info_full/$do_date
  ;;
"activity_rule")
  import_data /opt/module/datax/job/import/gmall.activity_rule.json /origin_data/gmall/db/activity_rule_full/$do_date
  ;;
"base_category1")
  import_data /opt/module/datax/job/import/gmall.base_category1.json /origin_data/gmall/db/base_category1_full/$do_date
  ;;
"base_category2")
  import_data /opt/module/datax/job/import/gmall.base_category2.json /origin_data/gmall/db/base_category2_full/$do_date
  ;;
"base_category3")
  import_data /opt/module/datax/job/import/gmall.base_category3.json /origin_data/gmall/db/base_category3_full/$do_date
  ;;
"base_dic")
  import_data /opt/module/datax/job/import/gmall.base_dic.json /origin_data/gmall/db/base_dic_full/$do_date
  ;;
"base_province")
  import_data /opt/module/datax/job/import/gmall.base_province.json /origin_data/gmall/db/base_province_full/$do_date
  ;;
"base_region")
  import_data /opt/module/datax/job/import/gmall.base_region.json /origin_data/gmall/db/base_region_full/$do_date
  ;;
"base_trademark")
  import_data /opt/module/datax/job/import/gmall.base_trademark.json /origin_data/gmall/db/base_trademark_full/$do_date
  ;;
"cart_info")
  import_data /opt/module/datax/job/import/gmall.cart_info.json /origin_data/gmall/db/cart_info_full/$do_date
  ;;
"coupon_info")
  import_data /opt/module/datax/job/import/gmall.coupon_info.json /origin_data/gmall/db/coupon_info_full/$do_date
  ;;
"sku_attr_value")
  import_data /opt/module/datax/job/import/gmall.sku_attr_value.json /origin_data/gmall/db/sku_attr_value_full/$do_date
  ;;
"sku_info")
  import_data /opt/module/datax/job/import/gmall.sku_info.json /origin_data/gmall/db/sku_info_full/$do_date
  ;;
"sku_sale_attr_value")
  import_data /opt/module/datax/job/import/gmall.sku_sale_attr_value.json /origin_data/gmall/db/sku_sale_attr_value_full/$do_date
  ;;
"spu_info")
  import_data /opt/module/datax/job/import/gmall.spu_info.json /origin_data/gmall/db/spu_info_full/$do_date
  ;;
"all")
  import_data /opt/module/datax/job/import/gmall.activity_info.json /origin_data/gmall/db/activity_info_full/$do_date
  import_data /opt/module/datax/job/import/gmall.activity_rule.json /origin_data/gmall/db/activity_rule_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_category1.json /origin_data/gmall/db/base_category1_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_category2.json /origin_data/gmall/db/base_category2_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_category3.json /origin_data/gmall/db/base_category3_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_dic.json /origin_data/gmall/db/base_dic_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_province.json /origin_data/gmall/db/base_province_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_region.json /origin_data/gmall/db/base_region_full/$do_date
  import_data /opt/module/datax/job/import/gmall.base_trademark.json /origin_data/gmall/db/base_trademark_full/$do_date
  import_data /opt/module/datax/job/import/gmall.cart_info.json /origin_data/gmall/db/cart_info_full/$do_date
  import_data /opt/module/datax/job/import/gmall.coupon_info.json /origin_data/gmall/db/coupon_info_full/$do_date
  import_data /opt/module/datax/job/import/gmall.sku_attr_value.json /origin_data/gmall/db/sku_attr_value_full/$do_date
  import_data /opt/module/datax/job/import/gmall.sku_info.json /origin_data/gmall/db/sku_info_full/$do_date
  import_data /opt/module/datax/job/import/gmall.sku_sale_attr_value.json /origin_data/gmall/db/sku_sale_attr_value_full/$do_date
  import_data /opt/module/datax/job/import/gmall.spu_info.json /origin_data/gmall/db/spu_info_full/$do_date
  ;;
esac

👉最后chmod 777 ~/bin/mysql_to_hdfs_full.sh

# 测试同步脚本
mysql_to_hdfs_full.sh all 2020-06-14
  • 查看HDFS目表路径是否出现全量表数据,全量表共15张在这里插入图片描述

3.3. 增量表数据同步(Maxwell、Flume:MySQL=>Kafka=>HDFS)

🔨数据通道如下图:
在这里插入图片描述
🔨Flume配置:Flume需要将Kafka中topic_db主题的数据传输到HDFS,故其需选用KafkaSource以及HDFSSink,Channel选用FileChannel。需要注意的是, HDFSSink需要将不同mysql业务表的数据写到不同的路径,并且路径中应当包含一层日期,用于区分每天的数据。关键配置如下:在这里插入图片描述

  • 具体数据示例如下:在这里插入图片描述

🔨104上创建Flume配置文件,cd /opt/module/flume-1.9.0/job/;vim kafka_to_hdfs_db.conf

a1.sources = r1
a1.channels = c1
a1.sinks = k1

a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.sources.r1.kafka.topics = topic_db
a1.sources.r1.kafka.consumer.group.id = flume
a1.sources.r1.setTopicHeader = true
a1.sources.r1.topicHeader = topic
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.ygy.gmall.flume.interceptor.TimestampAndTableNameInterceptor$Builder

a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume-1.9.0/checkpoint/behavior2
a1.channels.c1.dataDirs = /opt/module/flume-1.9.0/data/behavior2/
a1.channels.c1.maxFileSize = 2146435071
a1.channels.c1.capacity = 1000000
a1.channels.c1.keep-alive = 6

## sink1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/db/%{tableName}_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.sources.r1.channels = c1
a1.sinks.k1.channel= c1
  • 编写拦截器com.ygy.gmall.flume.interceptor包下创建TimestampAndTableNameInterceptor
package com.ygy.gmall.flume.interceptor;
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public class TimestampAndTableNameInterceptor implements Interceptor {
    @Override
    public void initialize() {}

    @Override
    public Event intercept(Event event) {

        Map<String, String> headers = event.getHeaders();
        String log = new String(event.getBody(), StandardCharsets.UTF_8);

        JSONObject jsonObject = JSONObject.parseObject(log);

        Long ts = jsonObject.getLong("ts");
        //Maxwell输出的数据中的ts字段时间戳单位为秒,Flume HDFSSink要求单位为毫秒
        String timeMills = String.valueOf(ts * 1000);
        String tableName = jsonObject.getString("table");

        headers.put("timestamp", timeMills);
        headers.put("tableName", tableName);
        return event;
    }

    @Override
    public List<Event> intercept(List<Event> events) {
        for (Event event : events) {
            intercept(event);
        }
        return events;
    }

    @Override
    public void close() {}

    public static class Builder implements Interceptor.Builder {

        @Override
        public Interceptor build() {
            return new TimestampAndTableNameInterceptor (); }

        @Override
        public void configure(Context context) {
        }
    }
}
  • 打好的包放入到hadoop104的/opt/module/flume-1.9.0/lib文件夹下
sftp root@hadoop104
lcd F:\Maven\flume-interceptor\target
cd /opt/module/flume-1.9.0/lib
put flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar

# 删除原来有哪个jar包的话要删掉

🌰测试一下:

# 102启动Zookeeper、Kafka集群、maxwell
zk.sh start;kf.sh start;mxw.sh start

# 启动hadoop104的Flume
cd /opt/module/flume-1.9.0/;bin/flume-ng agent -n a1 -c conf/ -f job/kafka_to_hdfs_db.conf -Dflume.root.logger=info,console

# 102生成模拟数据
cd /opt/module/db_log/;java -jar gmall2020-mock-db-2021-11-14.jar

👉点击前往hdfs查看是否生成了后缀为inc的表

仔细观察,会发现目标路径中的日期,并非模拟数据的业务日期,而是当前日期。这是由于Maxwell输出的JSON字符串中的ts字段的值,是数据的变动日期。而真实场景下,数据的业务日期与变动日期应当是一致的

  • 102编写Flume增量数据同步启停脚本vim /root/bin/f3.sh
#!/bin/bash

case $1 in
"start")
        echo " --------启动 hadoop104 业务数据flume-------"
        ssh hadoop104 "nohup /opt/module/flume-1.9.0/bin/flume-ng agent -n a1 -c /opt/module/flume-1.9.0/conf -f /opt/module/flume-1.9.0/job/kafka_to_hdfs_db.conf >/dev/null 2>&1 &"
;;
"stop")

        echo " --------停止 hadoop104 业务数据flume-------"
        ssh hadoop104 "ps -ef | grep kafka_to_hdfs_db | grep -v grep |awk '{print \$2}' | xargs -n1 kill -9"
;;
esac

👉 chmod 777 /root/bin/f3.sh

  • Maxwell配置(生产环境中无需担心)
    在这里插入图片描述
    此处为了模拟真实环境,对Maxwell源码进行了改动,增加了一个参数mock_date,该参数的作用就是指定Maxwell输出JSON字符串的ts时间戳的日期,接下来进行测试
# 修改Maxwell配置文件config.properties,增加mock_date参数,如下
#注:该参数仅在maxwell教学版中存在,该参数仅供学习使用修改该参数后重启Maxwell才可生效
mock_date=2020-06-14

# 重启Maxwell
mxw.sh restart
# 重新生成模拟数据
cd /opt/module/db_log/;java -jar gmall2020-mock-db-2021-11-14.jar
# 观察HDFS目标路径日期是否正常
  • 增量表首日全量同步:通常情况下,增量表需要在首日进行一次全量同步,后续每日再进行增量同步,首日全量同步可以使用Maxwell的bootstrap功能,方便起见,102上编写一个增量表首日全量同步脚本vim /root/bin/mysql_to_kafka_inc_init.sh
#!/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

chmod 777 /root/bin/mysql_to_kafka_inc_init.sh,并且删掉hdfs上的origin_data目录在这里插入图片描述

🌰测试同步脚本

f3.sh start;mysql_to_kafka_inc_init.sh all

在这里插入图片描述在这里插入图片描述

2.4. 采集通道启动/停止脚本(仅学习用)

🔨102上vim /root/bin/cluster.sh

#!/bin/bash

case $1 in
"start"){
        echo ================== 启动 集群 ==================

        #启动 Zookeeper集群
        zk.sh start

        #启动 Hadoop集群
        myhadoop.sh.sh start

        #启动 Kafka采集集群
        kf.sh start

        #启动采集 Flume
        f1.sh start

#启动日志消费 Flume
        f2.sh start

#启动业务消费 Flume
        f3.sh start

#启动 maxwell
        mxw.sh start
		jpsall
        };;
"stop"){
        echo ================== 停止 集群 ==================

#停止 Maxwell
        mxw.sh stop

#停止 业务消费Flume
        f3.sh stop

#停止 日志消费Flume
        f2.sh stop

#停止 日志采集Flume
        f1.sh stop

        #停止 Kafka采集集群
        kf.sh stop

        #停止 Hadoop集群
        myhadoop.sh stop

        #停止 Zookeeper集群
        zk.sh stop
		jpsall
};;
esac

chmod 777 ~/bin/cluster.sh

🌰测试一下:

# 全都启动来
f1.sh start;f2.sh start
jpsall

# 全部关闭试试看
cluster.sh stop
jpsall

在这里插入图片描述

🤯没鲨光真服了!还鲨都莎不掉在这里插入图片描述
😅原来它父亲护着它!找到PPid全噶了

# 一个一个纠着沙
cat /proc/53721/status|grep -w PPid | awk '{print$2}' | xargs -n1 kill -9
cat /proc/54019/status|grep -w PPid | awk '{print$2}' | xargs -n1 kill -9
cat /proc/53789/status|grep -w PPid | awk '{print$2}' | xargs -n1 kill -9

最后记得启动cluster.sh start

四、离线数仓环境准备(hive安装配置)

1. 安装hive-3.1.2(资料中的)

一定要使用资料中的hive压缩包(修改过源码的,后续部署hive on spark之后才不会出现不兼容的问题!)

🔨先上传资料中的压缩包
在这里插入图片描述

# 如果原来安装了hive就改一下名
mv /opt/module/hive /opt/module/hive_3.1.3
# 解压
tar -zxvf /opt/software/apache-hive-3.1.2-bin.tar.gz -C /opt/module/
# 改名
mv /opt/module/apache-hive-3.1.2-bin/ /opt/module/hive

# 修改/etc/profile.d/my_env.sh,添加环境变量
vim /etc/profile.d/my_env.sh
#HIVE_HOME
export HIVE_HOME=/opt/module/hive
export PATH=$PATH:$HIVE_HOME/bin

# 刷新一下环境
source /etc/profile.d/my_env.sh

# 解决日志Jar包冲突,进入/opt/module/hive/lib目录解决日志Jar包冲突,进入/opt/module/hive/lib目录
cd /opt/module/hive/lib;mv log4j-slf4j-impl-2.10.0.jar log4j-slf4j-impl-2.10.0.jar.bak

2. Hive元数据配置到MySQL

🔨驱动jar包(到lib目录下适用MySQL8.0以上版本):驱动jar包==>mysql-connector-j-8.1.0.jar下载👉详见
在这里插入图片描述

🔨配置Metastore到MySQL

# 在$HIVE_HOME/conf目录下新建hive-site.xml文件
vim /opt/module/hive/conf/hive-site.xml

🌰其中内容需要修改成自己的,仅供参考

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <!-- jdbc连接的URL -->
    <property>
      <name>javax.jdo.option.ConnectionURL</name>
      <value>jdbc:mysql://192.168.150.103:3306/metastore?createDatabaseIfNotExist=true&amp;useUnicode=true&amp;characterEncoding=UTF-8</value>
  </property>
    
    <!-- jdbc连接的Driver-->
  <property>
      <name>javax.jdo.option.ConnectionDriverName</name>
      <value>com.mysql.cj.jdbc.Driver</value>
  </property>
    
	<!-- jdbc连接的username-->
    <property>
        <name>javax.jdo.option.ConnectionUserName</name>
        <value>root</value>
    </property>

    <!-- jdbc连接的password -->
    <property>
        <name>javax.jdo.option.ConnectionPassword</name>
        <value>lovexw999</value>
    </property>

    <!-- Hive默认在HDFS的工作目录 -->
    <property>
        <name>hive.metastore.warehouse.dir</name>
        <value>/user/hive/warehouse</value>
    </property>
    
	<property>
        <name>hive.metastore.schema.verification</name>
        <value>false</value>
    </property>

    <property>
    <name>hive.server2.thrift.port</name>
    <value>10000</value>
    </property>

    <property>
        <name>hive.server2.thrift.bind.host</name>
        <value>hadoop102</value>
    </property>

    <property>
        <name>hive.metastore.event.db.notification.api.auth</name>
        <value>false</value>
    </property>
    
    <property>
        <name>hive.cli.print.header</name>
        <value>true</value>
    </property>

    <property>
        <name>hive.cli.print.current.db</name>
        <value>true</value>
    </property>
    
    <!-- 用于解析 JSON 格式的文件-->
    <property>
		<name>metastore.storage.schema.reader.impl</name>
		<value>org.apache.hadoop.hive.metastore.SerDeStorageSchemaReader</value>
	</property>
</configuration>

后续只要修改了 hive-site.xml,就必须重启 hiveserver2

🔨初始化Hive元数据库(修改为采用MySQL存储元数据)

# 登录MySQL
mysql -uroot -p
show databases;
# 如果有了Metastore则删除重建一个
DROP DATABASE metastore;
create database metastore;
quit;

# 执行初始化操作
cd /opt/module/hive/;bin/schematool -dbType mysql -initSchema -verbose

在这里插入图片描述

在三检查避免出Bug:

  1. jdbc连接的URL:要符合自己情况的!!
  2. jdbc连接的Driver:在Mysql 8及以上版本中,驱动类已经从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver,在Mysql 8及以上版本中,虽然兼容老版本,但是推荐新的
  3. 驱动jar包:要找到对应当前MySQL版本的jar包
  4. jdbc连接的password:免密登录的要改过,详见👉这里

🔨修改元数据库字符集:Hive元数据库的字符集默认为Latin1,由于其不支持中文字符,故若建表语句中包含中文注释,会出现乱码现象。如需解决乱码问题,须做以下修改(修改Hive元数据库中存储注释的字段的字符集为utf-8)

-- 进入mysql
use metastore;
-- 字段注释
alter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;
-- 表注释
alter table TABLE_PARAMS modify column PARAM_VALUE mediumtext character set utf8;
-- 退出
quit

🌰测试一下:

# 记得先启动hadoop集群
hive
hive (default)> show databases;

在这里插入图片描述

👉hive远程连接(DataGirp)启动有困难就来看他!!!


总结

在这里插入图片描述

✍至此把数仓需要的组件全部下好,前置学习完成,下一篇,电商数据仓库系统!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值