实际生产环境中,我们要处理的数据来自可能各个地方,业务数据库,爬虫数据库,日志文件,api网关买入数据等。
本次黑马头条推荐项目中,业务数据存储在mysql中,用户行为数据存储在日志中,因此采用两种技术手段将业务数据和日志数据传输到Hadoop中。
业务数据存储在mysql中,为了避免直接操作业务数据,利用sqoop导入到hive表中(底层数据就是存储在HDFS上)
业务数据不是一次导入就结束,每天都会产生很多新的业务数据,因此这里就涉及到利用sqoop导入数据到hive的方式。
Sqoop(发音:skup)是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql、postgresql…)间进行数据的传递,可以将一个关系型数据库(例如 : MySQL ,Oracle ,Postgres等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。
【Sqoop支持两种方式的数据导入,即全量数据导入和增量数据导入】。
(1)概念:如同名字那样,全量数据导入就是一次性将所有需要的数据,从关系型数据库一次性导入到Hadoop生态中(可以是HDFS,Hive,Hbase等)。
(2)适用场景:一次性离线分析场景
(3)代码实现:用sqoop import命令,具体如下:
# 全量数据导入
Sqoop import
1. --connect jdbc:mysql://192.168.15.111/toutiao \
2. --username root \
3. --password 123456 \
4. --table $table_name \
5. --query ‘select * from $table_name where $conditions ’ \
6. --m 5 \
7. --hive-import \
8. --hive-home /usr/local/hive \
9. --hive -drop-import-delims \
10. --target-dir /user/hive/warehouse \
11. --create-hive-table \
12. --hive-table toutiao.$table_name \
(1)使用场景:实际生产环境中,不断有业务相关的数据产生到关系型数据库,系统需要定期从数据库向hadoop导入数据,导入数仓后,继续进行离线分析。我们不可能将所有的数据重新导一遍,此时就需要用sqoop的增量数据导入模式。
增量数据导入分两种,一是基于递增列的增量数据导入(Append),二是基于时间序列的增量数据导入(LastModified)
举个栗子,有一个订单表,里面每个订单有一个唯一标识自增列ID,在关系型数据库中以主键形式存在。之前已经将id在0~5201314之间的编号的订单导入到Hadoop中了(这里为HDFS),一段时间后我们需要将近期产生的新的订单数据导入Hadoop中(这里为HDFS),以供后续数仓进行分析。此时我们只需要指定–incremental 参数为append,–last-value参数为5201314即可,表示只从id大于5201314后开始导入。
1. sqoop import \
2. --connect jdbc:mysql://192.168.15.111:3306/test \ #连接到指定数据库
3. --username root \
4. --password 123456 \
5. --table order_table \ # 指定数据库的指定表
6. --target-dir /user/mysql_to_hdfs \
7. --m 3 \
8. –-hive import \
9. –-incremental append \ # 指明模式
10. –-check-column order_id # 指明用于增量导入的参考列
11. –-last-value 5201314 \ # 指定参考列上次导入的最大值
参数 | 说明 |
---|---|
–incremental append | 基于递增列的增量导入(将递增列值大于阈值的所有数据增量导入Hadoop) |
–check-column | 递增列(int) |
–last-value | 阈值(int) |
此方式要求原有表中有time字段,它能指定一个时间戳,让Sqoop把该时间戳之后的数据导入至Hadoop(这里为HDFS)。比如我的头条业务数据库中,某篇文章的点赞数增加或减少了,变成了一个新的数据,在我指定的时间戳后面作为新的业务数据导入到了Hadoop(这里指HDFS),我们可以指定给merge-key参数,例如是article_id,表示将后续的新的记录与原有的记录合并。
代码:
sqoop import \
1. --connect jdbc:mysql://192.168.15.111/toutiao \
2. --username root \
3. --password password \
4. --table article_basic \ # 指定数据表导入Hadoop
5. --m 4 \
6. --target-dir /user/hive/warehouse/toutiao.db/article_basic \
7. --incremental lastmodified \
8. --check-column update_time \
9. --merge-key article_id \
10. --last-value '2012-02-01 11:0:00'
重要参数
参数 | 说明 |
---|---|
–incremental lastmodified | 基于时间列的增量导入(将时间列大于等于阈值的所有数据增量导入Hadoop) |
–check-column | 时间列(int) |
–last-value | 阈值(int) |
–merge-key | 合并列(主键,合并键值相同的记录) |
这里注意-incremental lastmodified 模式不支持用sqoop直接导入到hive中,需要先导入到hdfs,然后建立hive表关联。
那怎么才能实现mysql数据迁移到hive上呢?
Sqoop将mysql数据导入到HDFS上,指定位置:
–target-dir /user/hive/warehouse/toutiao.db/
然后进入hive交互界面,在toutiao.db数据库中建表,表名和传上来的数据文件一样,hive就能自动将数据映射到hive表中。
1、注意:sqoop将数据导出的hdfs分片数据,默认用‘ ,’分割,而hive默认的分隔符是’ 001’。所以在hive中创建表的时候要指定分隔符。
2、原mysql中某些字段存在特定字符,如,、\t \n 都会导致导入到hadoop被hive读取失败,解
析时会认为是另一条数据,或者多一个字段。
///解决办法///:
导入时,加入query参数,选择特定字段,过滤相应内容,使用replace,char替换字符
3、mysql数据库里面字段是tinyint类型,通过sqool导入到hdfs,hive建表映射数据后,该字段却显示True,False,这是因为jdbc会把tinyint认为是java.sql.Types.BIT,然后hive就转为Boolean类型了。
///解决办法///:
在connect中加入一句话就可以了 ?tinyInt1isBit t=false 就行了,例如:
–connect jdbc:mysql://192.168.15.111/toutiao?tinyInt1isBit=false
关于脚本的执行
因为业务数据每天都会产生,因此每天都要导入数据到HDFS,根据各个公司和实际生产要求,可能是每天定点导一次数据(这个还可以认为定时导数据),也可能是每天多个时间定点导数据,这样就不适合人为导,应该利用程序自动导数据,这里我们利用一个Linux命令:crontab,设定时间自动导数据。
首先简要介绍一下crontab命令
Linux crontab是用来定期执行程序的命令
当安装完成操作系统之后,默认便会启动此任务调度命令
crond 命令每分钟会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。
新创建的 cron 任务,不会马上执行,至少要过 2 分钟后才可以,当然你可以重启 cron 来马上执行。而 linux 任务调度的工作主要分为以下两类:
1、系统周期性所要执行的工作,比如写缓存数据到硬盘、日志清理等。在/etc目录下有一个crontab文件,这个就是系统任务调度的配置文件。
2、个人执行的工作:某个用户定期要做的工作,例如每隔10分钟检查邮件服务器是否有新信,这些工作可由每个用户自行设置。
cron通过 /etc/cron.allow 和 /etc/cron.deny 文件来限制某些用户是否可以使用 crontab 命令,
(1)当系统中有 /etc/cron.allow 文件时,只有写入此文件的用户可以使用 crontab 命令,没有写入的用户不能使用 crontab 命令。同样,如果有此文件,/etc/cron.deny 文件会被忽略,因为 /etc/cron.allow 文件的优先级更高。
(2)当系统中只有 /etc/cron.deny 文件时,写入此文件的用户不能使用 crontab 命令,没有写入文件的用户可以使用 crontab 命令。
(3)crontab文件都位于/var/spool/cron/目录中
第一步:创建crontab文件
[root@localhost !]$ crontab -e
显示结果
#进入 crontab 编辑界面。会打开Vim编辑你的任务
* * * * * 执行的任务
执行这个命令的时候,打开的是一个空文件,操作方法和vim一样,文件里面填写需要执行的任务。
第二步:在文件中写下需要执行的任务
* * * * * 执行的任务
这里分两部分来学习,第一部分的五个*,表示设定的时间,第二部分表示要执行的commad或者某目录中的脚本。
这里的command可以是一个简单的指令,例如启动某功能,也可以是指定目录下的脚本。
【使用示例】
实例1:每1分钟执行一次重启smb
命令:* * * * * /etc/init.d/smb restart
实例2:每两小时重启smb
命令:* */2 * * * /etc/init.d/smb restart
实例3:晚上11点到早上7点之间,每隔一小时重启smb
命令:* 23-7/1 * * * /etc/init.d/smb restart
实例4:隔半小时从mysql增量导入一次数据到hadoop
命令:* */0.5 * * * /root/toutiao_project/scripts/import_incremental.sh
—补充—
flume运行的最小单元,独立运行在一个JVM中。一个agent里面包括一个或多个sources,channels,sinks,每个agent内部有三个组件。
1)souce:数据采集组件,对接我们的源数据
2)channel:传输通道组件,通俗叫管道,数据的缓冲区,连接source和sink,将source和sink进行打通
3)sink:下沉组件,用于向下一级agent传递数据或者往最终的存储系统传递数据。
1.下载
Wget https://mirrors.tuna.tsinghua.edu.cn/apache/flume/1.9.0/apache-flume-1.9.0-bin.tar.gz
2.解压
tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /usr/local
3.重命名
mv apache-flume-1.9.0-bin.tar flume
4.配置环境变量
vi /etc/profile
添加以下信息
export FLUME_HOME=/usr/local/flume
export PATH=$PATH:$FLUME_HOME/bin
5.验证环境变零
source /etc/profil
6.修改flume的conf目录下文件,flume-env.sh,如果没有,就复制flume-env.sh.template
vi flume-env.sh
修改java的路径信息配置
export JAVA_HOME=/usr/local/java
7.校验是否安装成功,进入bin目录
./flume-ng version
1) 案例需求
使用flume监听一个端口,收集该端口的数据,并打印到控制台
2) 案例分析
① 通过netcat工具向本机的44444(随机选取的,如果hadoop开了,就不要选取8070等特殊的端口,因为这些端口已经在使用了)端口发送数据,例如hell
②通过flume监控本机的4444端口,通过flume的source端读取数据。
③flume将获取的数据通过sink端口直接写到控制台。
3)操作
Flume不需要写代码,都是写配置文件进行操作。
第一步:
在flume目录下创建一个job文件夹,用于放置自己写的操作配置文件,在 job 文件夹下创建 Flume Agent 配置文件netcat-logger.conf
[root@bigdata111 flume]$ touch netcat_logger.conf
第二步:
在该文件中添加如下内容:
# 定义这个agent中各个组件的名字,这是固定写法
a1.sources = r1
a1.sinks = k1 a1.channels = c1
# 描述和配置source组件:r1
a1.sources.r1.type = netcat
a1.sources.r1.bind =192.168.15.111
a1.sources.r1.port = 44444
# 描述和配置sink组件:k1
a1.sinks.k1.type = logger
# 描述和配置channel组件,此处采用的是内存缓存的方式
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 描述和配置source channel sink之间的连接关系
a1.sources.r1.channels = c1 a1.sinks.k1.channel = c1
第三步:
安装talent测试
yum -y install telnet
第四步:
实验验证,进入flume目录,执行命令:
bin/flume-ng agent -c conf -f job/netcat-logger.conf -n a1 -Dflume.root.logger=INFO,console
执行的命令解释:
bin/flume-ng:目录下有个flume-ng脚本,
agent:启动一个agent
-c conf:表示使用我们自己配置的conf文件
-f job/netcat-logger.conf:我们自己的agent文件路径,这里填的是相对路径
-n a1:给agent起个名字,叫a1,注意这个名字我们netcat-logger.conf文件里面的名字一致
最后就是一些常规的启动日志参数,这里表示把启动日志都打印到控制台
启动监控功能,出现以下画面即为成功:
另开一个窗口,执行以下指令:
telnet 192.168.15.111 44444
在该端口任意输入,例如hello python,在另个监控端口就能监控到。
总结:【以上是通过exec监听数据源,这种方式虽然实时性较高,但是可靠性较差,当source程序运行异常或者linux命令中断,都会造成数据的丢失,再恢复正常运行之前,数据的完整性无法得到保证。
Spooling Directory Source通过监听某个目录下的新增文件,并将文件的内容读取出来,实现日志信息的收集。实际使用中会结合log4j进行使用。被传输结束的文件会修改后缀名,添加.completed后缀(可以自定义)。】
1)案例需求
监控某一个文件夹下面的所有文件,只要这个目录下面有文件,收集文件内容,上传到HDFS上。
2) 案例分析
source:监看某个文件夹下的文件,发生变化,就收集文件内容到HDFS.
channel:memory channel(内存缓存)
sink:用HDFS的sink(这样数据才能到HDFS上)
3) 编写agent配置文件
进入flume的job文件夹,新建文件spooldir.conf文件,将上述agent的配置内容写进入
# 定义这个agent中各个组件的名字,这里agent取名a1,三个组件取名r1,k1,c1
a1.sources = r1 # 定义source
a1.sinks = k1 # 定义 sink
a1.channels = c1 # 定义 channel
# 描述和配置source组件r1,注意不能往监控目录中丢重复同名文件呢
a1.sources.r1.type=spooldir # 定义source类型为目录
a1.sources.r1.spoolDir=/usr/data_temp/flume # 定义监控目录
a1.sources.r1.fileSuffix =.completed # 定义文件上传结束,后缀
a1.sources.r1.fileHeader=true # 定义是否有文件头 ,可加,可不加
# 描述和配置sink组件k1
a1.sinks.k1.type=hdfs # sink类型为hdfs
a1.sinks.k1.path=hdfs://localhost:9000 \
/user/spooldir/file/%y-%m-%d/%H%M/ \
a1.sinks.k1.filePrefix=upload- # 上传文件到hdfs的前缀
#文件的采集策略,多长时间采集一次,文件多大采集一次
a1.sinks.k1.hdfs.round=true # 是否按时间滚动文件
a1.sinks.k1.hdfs.roundValue=1 # 多长时间单位创建一个新的文件夹
a1.sinks.k1.hdfs.roundUnit=minute # 重新定义时间单位
a1.sinks.k1.hdfs.useLocalTimeStamp = true # 是否使用本地时间戳
a1.sinks.k1.hdfs.rollInterval=10 # 多久生成新文件,单位是多少秒
a1.sinks.k1.hdfs.rollSize=20 # 多大生成新文件
a1.sinks.k1.hdfs.collCount=0 #多少个event生成新文件
a1.sinks.k1.hdfs.minBlockReplicas=1 # 生成多少个副本
# 生成的文件类型,默认是Sequencefile,可以用DataStream(就是普通文本)
a1.sinks.k1.hdfs.fileType=DataStream
#描述和配置channel组件:c1
a1.channels.c1.type=memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 描述和配置source channel sink之间的连接关系
a1.sources.r1.channels=c1
a1.sinks.k1.channel =c1
将数据放到hdfs上,要避免产生大量的小文件,通过控制文件采集策略,一般设置两种采集策略,例如文件127.9M采集一次,两个小时滚动一次。
4)启动agent
进入flume目录
bin/flume-ng agent -c conf -f job/spooldir.conf -n a1 -Dflume.root.logger=INFO, console
只要我们指定的目录/usr/data_temp/flume下有文件变化,就会被flume采集到HDFS上。