hadoop系列十八——案例 App数据统计与报表统计

项目背景

网站、app的运营者需要知道自己的产品或服务的运营状况,就需要对使用自己产品的用户进行各种角度的数据分析,比如:
用户数量
新增用户
留存用户
活跃用户
地域分析
渠道分析

要做这样的分析,数据来源应该是用户的产品使用的行为日志,行为日志是由app或者网站的页面获取用户相关信息后,发送给后台服务器记录下来的:
hadoop系列十八——案例 App数据统计与报表统计_第1张图片

项目步骤

1.采集数据 (待完善)

从服务器通过flume agent 采集日志,将数据采集到HDFS,生产目录
/applog/2017-09-20/… (512M一个文件)

2.数据预处理(MapReduce)

预处理

需求:

1.清洗:检查每条日志的必选字段是否完整,不完整的日志应该滤除

2.数据解析成原始:
为每条日志添加一个用户唯一标识字段:user_id
user_id的取值逻辑:
如果是ios设备,user_id=device_id
如果是android设备, user_id = android_id
如果android_id为空,则user_id = device_id

3、将event字段抛弃,将header中的各字段解析成普通文本行

主要技术点:json解析 gson/fastjson/jackson/…

4.还有一个变态需求:
需要将清洗后的结果数据,分ios和android和其他 三种类别,输出到3个不同的文件夹;

/app_clean_log/2017-09-20/ios/part-r-00000
/ios/part-r-00000
/ios/part-r-00001

/android/part-r-00000
/android/part-r-00001

/other/part-r-00000
/other/part-r-00001

百度:multipleOutputs

实现:

MapReduce代码

public class AppLogDataClean {

	public static class AppLogDataCleanMapper extends Mapper<LongWritable, Text, Text, NullWritable> { 
	//在这里提前声明需要的东西,不需要map()方法里面反复声明浪费资源,但是不可以传入外部参数
		Text k = null;
		NullWritable v = null;
		SimpleDateFormat sdf = null;
		MultipleOutputs<Text,NullWritable> mos = null;  //多路输出器
		
		@Override
		protected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)
				throws IOException, InterruptedException {
					//在这里提前声明需要的东西,不需要map()方法里面反复声明浪费资源,在setup()方法中可以传入外部参数
			k = new Text();
			v = NullWritable.get();
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			mos = new MultipleOutputs<Text,NullWritable>(context);
		}
		

		@Override
		protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context)
				throws IOException, InterruptedException {

			JSONObject jsonObj = JSON.parseObject(value.toString());  //将value变成一个jasonObject,可理解为一个hashmap

			JSONObject headerObj = jsonObj.getJSONObject(GlobalConstants.HEADER);   //首先先get header这个key对应的value,作为jsonObject
			//GlobalConstants.HEADER意思是将这个参数改成一个常量类
			

			/**
			 * 过滤缺失必选字段的记录
			 * 1、检查每条日志的必选字段是否完整,不完整的日志应该滤除
			 */
			if (StringUtils.isBlank(headerObj.getString("sdk_ver"))) {
				return;   //如果根据"sdk_ver"这个key get到的value为null或者空字符串“”,那么什么都不返回
			}

			if (null == headerObj.getString("time_zone") || "".equals(headerObj.getString("time_zone").trim())) {
				return;
			}

			if (null == headerObj.getString("commit_id") || "".equals(headerObj.getString("commit_id").trim())) {
				return;
			}

			if (null == headerObj.getString("commit_time") || "".equals(headerObj.getString("commit_time").trim())) {
				return;
			}else{
				// 练习时追加的逻辑,替换掉原始数据中的时间戳
				String commit_time = headerObj.getString("commit_time");
				String format = sdf.format(new Date(Long.parseLong(commit_time)+38*24*60*60*1000L));
				headerObj.put("commit_time", format);
				
			}

			if (null == headerObj.getString("pid") || "".equals(headerObj.getString("pid").trim())) {
				return;
			}

			if (null == headerObj.getString("app_token") || "".equals(headerObj.getString("app_token").trim())) {
				return;
			}

			if (null == headerObj.getString("app_id") || "".equals(headerObj.getString("app_id").trim())) {
				return;
			}

			if (null == headerObj.getString("device_id") || headerObj.getString("device_id").length()<17) {
				return;
			}

			if (null == headerObj.getString("device_id_type")
					|| "".equals(headerObj.getString("device_id_type").trim())) {
				return;
			}

			if (null == headerObj.getString("release_channel")
					|| "".equals(headerObj.getString("release_channel").trim())) {
				return;
			}

			if (null == headerObj.getString("app_ver_name") || "".equals(headerObj.getString("app_ver_name").trim())) {
				return;
			}

			if (null == headerObj.getString("app_ver_code") || "".equals(headerObj.getString("app_ver_code").trim())) {
				return;
			}

			if (null == headerObj.getString("os_name") || "".equals(headerObj.getString("os_name").trim())) {
				return;
			}

			if (null == headerObj.getString("os_ver") || "".equals(headerObj.getString("os_ver").trim())) {
				return;
			}

			if (null == headerObj.getString("language") || "".equals(headerObj.getString("language").trim())) {
				return;
			}

			if (null == headerObj.getString("country") || "".equals(headerObj.getString("country").trim())) {
				return;
			}

			if (null == headerObj.getString("manufacture") || "".equals(headerObj.getString("manufacture").trim())) {
				return;
			}

			if (null == headerObj.getString("device_model") || "".equals(headerObj.getString("device_model").trim())) {
				return;
			}

			if (null == headerObj.getString("resolution") || "".equals(headerObj.getString("resolution").trim())) {
				return;
			}

			if (null == headerObj.getString("net_type") || "".equals(headerObj.getString("net_type").trim())) {
				return;
			}

			/**
			 * 生成user_id
			 * 为每条日志添加一个用户唯一标识字段:user_id
             *user_id的取值逻辑:
             *如果是ios设备,user_id=device_id
             *如果是android设备, user_id = android_id
    如果android_id为空,则user_id = device_id

			 */
			String user_id = "";
			if ("android".equals(headerObj.getString("os_name").trim())) {
				user_id = StringUtils.isNotBlank(headerObj.getString("android_id")) ? headerObj.getString("android_id")
						: headerObj.getString("device_id");
			} else {
				user_id = headerObj.getString("device_id");
			}
			

			/**
			 * 输出结果
			 *  JsonToStringUtil.toString()是工具类,把一条记录的所有value拼接起来,把jsonObject 转化为String,然后输出
			 * 需要将清洗后的结果数据,分ios和android和其他  三种类别,输出到3个不同的文件夹;
			 
			 */
			headerObj.put("user_id", user_id);
			k.set(JsonToStringUtil.toString(headerObj));
			
			if("android".equals(headerObj.getString("os_name"))){
				mos.write(k, v, "android/android");        //第三个参数是输出路径
			}else{
				mos.write(k, v, "ios/ios");
			}

		}
		
		
		
		@Override
		protected void cleanup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)
				throws IOException, InterruptedException {
			mos.close();
		}

	}

	public static void main(String[] args) throws Exception {

		Configuration conf = new Configuration();

		Job job = Job.getInstance(conf);

		job.setJarByClass(AppLogDataClean.class);

		job.setMapperClass(AppLogDataCleanMapper.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(NullWritable.class);

		job.setNumReduceTasks(0);

		// 避免生成默认的part-m-00000等文件,因为,数据已经交给MultipleOutputs输出了
		LazyOutputFormat.setOutputFormatClass(job, TextOutputFormat.class);
		
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));

		boolean res = job.waitForCompletion(true);
		System.exit(res ? 0 : 1);

	}

}

JsonToStringUtil工具类

package cn.edu360.app.log.mr;

import com.alibaba.fastjson.JSONObject;

public class JsonToStringUtil {

	public static String toString(JSONObject jsonObj) {

		StringBuilder sb = new StringBuilder();   
//然后将所有value拼接成一个长String串,转化为String数据,

sb.append(jsonObj.get("sdk_ver")).append("\001").append(jsonObj.get("time_zone")).append("\001")
				.append(jsonObj.get("commit_id")).append("\001").append(jsonObj.get("commit_time")).append("\001")
				.append(jsonObj.get("pid")).append("\001").append(jsonObj.get("app_token")).append("\001")
				.append(jsonObj.get("app_id")).append("\001").append(jsonObj.get("device_id")).append("\001")
				.append(jsonObj.get("device_id_type")).append("\001").append(jsonObj.get("release_channel"))
				.append("\001").append(jsonObj.get("app_ver_name")).append("\001").append(jsonObj.get("app_ver_code"))
				.append("\001").append(jsonObj.get("os_name")).append("\001").append(jsonObj.get("os_ver"))
				.append("\001").append(jsonObj.get("language")).append("\001").append(jsonObj.get("country"))
				.append("\001").append(jsonObj.get("manufacture")).append("\001").append(jsonObj.get("device_model"))
				.append("\001").append(jsonObj.get("resolution")).append("\001").append(jsonObj.get("net_type"))
				.append("\001").append(jsonObj.get("account")).append("\001").append(jsonObj.get("app_device_id"))
				.append("\001").append(jsonObj.get("mac")).append("\001").append(jsonObj.get("android_id"))
				.append("\001").append(jsonObj.get("imei")).append("\001").append(jsonObj.get("cid_sn")).append("\001")
				.append(jsonObj.get("build_num")).append("\001").append(jsonObj.get("mobile_data_type")).append("\001")
				.append(jsonObj.get("promotion_channel")).append("\001").append(jsonObj.get("carrier")).append("\001")
				.append(jsonObj.get("city")).append("\001").append(jsonObj.get("user_id"));
		
		return sb.toString();

	}

}

预处理程序的脚本开发

功能:每日00:20启动预处理程序(MapRedcue),对数据进行解析,处理数据## 3. 数据入库
在同一hdfs,等预处理过程结束,将预处理之后的数据,导入hive中的事先建 好的表(外部表),导入当日分区
提示:
1、 在hive中建表ods_app_log(外部表、分区表)映射预处理阶段生成的清洗数据
2、 每天定时将预处理之后的天数据 导入到 ods_app_log的天分区 中

实现

4.统计与分析(hive)

1. 活跃用户(日活)

(1) 活跃用户信息表(比较原始的表)

把当天的活跃用户信息抽取出来,存入一个日活用户信息表

CREATE TABLE etl_user_active_day (
    sdk_ver string
    ,time_zone string
    ,commit_id string
    ,commit_time string
    ,pid string
    ,app_token string
    ,app_id string
    ,device_id string
    ,device_id_type string
    ,release_channel string
    ,app_ver_name string
    ,app_ver_code string
    ,os_name string
    ,os_ver string
    ,language string
    ,country string
    ,manufacture string
    ,device_model string
    ,resolution string
    ,net_type string
    ,account string
    ,app_device_id string
    ,mac string
    ,android_id string
    ,imei string
    ,cid_sn string
    ,build_num string
    ,mobile_data_type string
    ,promotion_channel string
    ,carrier string
    ,city string
    ,user_id string
    ) partitioned BY (day string) row format delimited fields terminated BY '\001';

(2) 活跃用户总表


概念:某一天使用过app的用户就是活跃用户

计算过程:

 源表——ods_app_log
 目标表:日活表
create table etl_user_active_day like ods_app_log;

注意:

  1. 每个活跃用户抽取他当天所有记录中时间最早的一条(用分组排序的语法)

  2. 从ods_app_log原始数据表的当天分区中,抽取当日的日活用户信息插入日活用户信息表etl_user_active_day

代码实现:

INSERT INTO TABLE etl_user_active_day PARTITION (day = '2017-09-21')
SELECT sdk_ver
    ,time_zone
    ,commit_id
    ,commit_time
    ,pid
    ,app_token
    ,app_id
    ,device_id
    ,device_id_type
    ,release_channel
    ,app_ver_name
    ,app_ver_code
    ,os_name
    ,os_ver
    ,LANGUAGE
    ,country
    ,manufacture
    ,device_model
    ,resolution
    ,net_type
    ,account
    ,app_device_id
    ,mac
    ,android_id
    ,imei
    ,cid_sn
    ,build_num
    ,mobile_data_type
    ,promotion_channel
    ,carrier
    ,city
    ,user_id
FROM (
SELECT *
,row_number() OVER (PARTITION BY user_id ORDER BY commit_time) AS rn  --分组加排序
 FROM ods_app_log WHERE day = '2017-09-21') tmp
WHERE rn = 1;
(3)各维度统计活跃用户总表

根据四个重要指标,以不同维度统计活跃用户总表
–各维度组合分析:
不区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 不区分城市city 区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 不区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户
区分操作系统os_name 区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户

像这样

维度组合统计
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
注意:
根据dim和day分区,表示以什么为组合和时间

  1. 统计不区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name活跃用户 这个维度的日活表

    -- 1 日新维度统计报表--数据建模 create table dim_user_new_day(os_name string,city string,release_channel string,app_ver_name string,cnts
    int) partitioned by (day string, dim string);
    
    
    -- 2 日新维度统计报表sql开发(利用多重插入语法) from etl_user_new_day
    
    insert into table dim_user_new_day
    partition(day='2017-09-21',dim='0000') select
    'all','all','all','all',count(1) -- where day='2017-09-21'
    
    
  2. 不区分操作系统os_name 不区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户 这个维度的日活表

    partition(day='2017-09-21',dim='0001') select
    'all','all','all',app_ver_name,count(1) where day='2017-09-21' 
    group by app_ver_name
    
    
  3. 不区分操作系统os_name 区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户 这个维度的日活表

    partition(day='2017-09-21',dim='0001') select
    'all','all',city,app_ver_name,count(1) where day='2017-09-21' 
    group by app_ver_name,city
    
    
    
    

1. 新增用户统计

新增用户的定义:
比如,在2017-08-28日出现了一些以前从没出现过的用户,则这些用户就是2017-08-28日的新增用户

需求:
1、将每日的新增用户从ods_app_log表中抽取出来,存入一个新用户信息表:
dw_new_user_day的日分区中

2、统计如下报表:
某日 城市 渠道 版本 新增用户数
2017-08-28 all all all ?
2017-08-28 具体城市 all all ?
2017-08-28 all 具体渠道 all ?
2017-08-28 all all 具体版本 ?
2017-08-28 all 具体渠道 具体版本 ?
2017-08-28 具体城市 all 具体版本 ?
2017-08-28 具体城市 具体渠道 all ?
2017-08-28 具体城市 具体渠道 具体版本 ?

整体思路:

  • a、应该建立一个历史用户表(只存user_id,第一天全部存入)

  • b、将当日的活跃用户去 比对 历史用户表, 就知道哪些人是今天新出现的用户 --> 当日新增用户(用联结的方式去对比,如果今日的用户表userID没有,那就是新用户)

  • c、将当日新增用户追加到历史用户表

(1)建立日新维度表实现

-- 1 历史用户表
create table etl_user_history(user_id string);  --(只存user_id)


-- 2 当日新增用户表:存所有字段(每个人时间最早的一条),带有一个分区字段:day string;
create table etl_user_new_day like etl_user_active_day;

-- 统计实现 *********************************

-- 1 当日活跃-历史用户表 --> 新增用户表的当日分区
insert  into etl_user_new_day partition(day='2017-09-21')
SELECT sdk_ver
    ,time_zone
    ,commit_id
    ,commit_time
    ,pid
    ,app_token
    ,app_id
    ,device_id
    ,device_id_type
    ,release_channel
    ,app_ver_name
    ,app_ver_code
    ,os_name
    ,os_ver
    ,LANGUAGE
    ,country
    ,manufacture
    ,device_model
    ,resolution
    ,net_type
    ,account
    ,app_device_id
    ,mac
    ,android_id
    ,imei
    ,cid_sn
    ,build_num
    ,mobile_data_type
    ,promotion_channel
    ,carrier
    ,city
    ,a.user_id
from  etl_user_active_day a left join  etl_user_history b on a.user_id = b.user_id
where a.day='2017-09-21' and b.user_id is null;


-- 2 将当日新增用户的user_id追加到历史表
insert into table etl_user_history
select user_id from etl_user_new_day where day='2017-09-21';

(2)日新用户表维度统计
from etl_user_new_day

insert into table dim_user_new_day partition(day='2017-09-21',dim='0000')
select 'all','all','all','all',count(1)
where day='2017-09-21'


insert into table dim_user_new_day partition(day='2017-09-21',dim='0001')
select 'all','all','all',app_ver_name,count(1)
where day='2017-09-21'
group by app_ver_name



insert into table dim_user_new_day partition(day='2017-09-21',dim='0010')
select 'all','all',release_channel,'all',count(1)
where day='2017-09-21'
group by release_channel


insert into table dim_user_new_day partition(day='2017-09-21',dim='0011')
select 'all','all',release_channel,app_ver_name,count(1)
where day='2017-09-21'
group by release_channel,app_ver_name


insert into table dim_user_new_day partition(day='2017-09-21',dim='0100')
select 'all',city,'all','all',count(1)
where day='2017-09-21'
group by city

留存用户统计

概念:
比如,15号的新增用户,在16号又活跃了,这些用户就是次日留存用户;
比如,12号的新增用户,在15号又活跃了,这些用户就是3日留存用户;

需求:
——ETL:先抽取出次日留存用户,存入一个次日留存用户信息表,记录跟活跃用户表相同的字段;
——维度分析:统计各种维度下的留存用户数、留存用户比例

逻辑思路:昨天在新用户表中,今天在活跃用户表中 --> 今日的“次日留存用户”
12号在新增用户中,在15号在活跃用户表,这些用户就是3日留存用户;
那么将用id将两表内联结,两表都存在的话,那么就是留存用户(用左半联结效率略高 吧)

实现:

内联结:

-- 数据建模 
--建次日留存etl信息表:记录跟活跃用户表相同的字段

create table etl_user_keepalive_nextday like etl_user_active_day;

-- etl开发
insert into table etl_user_keepalive_nextday partition(day='2017-09-22')
select
     actuser.sdk_ver 
    ,actuser.time_zone 
    ,actuser.commit_id 
    ,actuser.commit_time 
    ,actuser.pid 
    ,actuser.app_token 
    ,actuser.app_id 
    ,actuser.device_id 
    ,actuser.device_id_type 
    ,actuser.release_channel 
    ,actuser.app_ver_name 
    ,actuser.app_ver_code 
    ,actuser.os_name 
    ,actuser.os_ver 
    ,actuser.language 
    ,actuser.country 
    ,actuser.manufacture 
    ,actuser.device_model 
    ,actuser.resolution 
    ,actuser.net_type 
    ,actuser.account 
    ,actuser.app_device_id 
    ,actuser.mac 
    ,actuser.android_id 
    ,actuser.imei 
    ,actuser.cid_sn 
    ,actuser.build_num 
    ,actuser.mobile_data_type 
    ,actuser.promotion_channel 
    ,actuser.carrier 
    ,actuser.city 
    ,actuser.user_id 


from etl_user_new_day newuser join etl_user_active_day actuser
on newuser.user_id = actuser.user_id
where newuser.day='2017-09-21' and actuser.day='2017-09-22';

用左半联结的方法实现(用左半连接效率略高):

insert into table etl_user_keepalive_nextday partition(day='2017-09-22')
select 
 sdk_ver 
,time_zone 
,commit_id 
,commit_time 
,pid 
,app_token 
,app_id 
,device_id 
,device_id_type 
,release_channel 
,app_ver_name 
,app_ver_code 
,os_name 
,os_ver 
,language 
,country 
,manufacture 
,device_model 
,resolution 
,net_type 
,account 
,app_device_id 
,mac 
,android_id 
,imei 
,cid_sn 
,build_num 
,mobile_data_type 
,promotion_channel 
,carrier 
,city 
,user_id 
from etl_user_new_day a left semi join etl_user_active_day b
on a.user_id = b.user_id and a.day='2017-09-21' and b.day='2017-09-22';

where a.day='2017-09-21' and b.day='2017-09-22'; // 注意:left semi join中,右表的引用不能出现在where条件中

沉默用户分析

概念:创建用户后,一段时间内(连续7天)没有使用过app的用户

思路:
比如,现在运算的是20号的报表,
用13号的新增用户 left join 活跃用户表的(14-20号分区)
取右表join后结果为null的用户

-- 1 创建沉默用户表
create table silent_user_7_day like etl_user_active_day;


-- 统计实现 *********************************

-- 2 7日活跃-历史用户表 -->沉默用户表的当日分区
insert  into silent_user_7_day partition(day='2017-09-21')
SELECT sdk_ver
    ,time_zone
    ,commit_id
    ,commit_time
    ,pid
    ,app_token
    ,app_id
    ,device_id
    ,device_id_type
    ,release_channel
    ,app_ver_name
    ,app_ver_code
    ,os_name
    ,os_ver
    ,LANGUAGE
    ,country
    ,manufacture
    ,device_model
    ,resolution
    ,net_type
    ,account
    ,cid_sn
    ,build_num
    ,mobile_data_type
    ,promotion_channel
    ,carrier
    ,city
    ,a.user_id
from  eetl_user_new_day a left join  etl_user_history b on a.user_id = b.user_id
where b.day='2017-09-21' and  b.day='2017-09-20'  b.day='2017-09-19' and b.day='2017-09-18' and b.day='2017-09-17' and b.day='2017-09-16' and b.day='2017-09-15' and b.user_id is null;




版本升级轨迹

需求:每天统计出如下报表
日期 user_id app_token channel city source_ver curr_ver
示例:
2017-08-14,许老师,共享女友,360应用,北京,v1.0
2017-08-14,赵老师,共享女友,安智市场,北京,v1.2
2017-08-14,许老师,共享女友,360应用,天津,v1.2
2017-08-14,许老师,共享女友,小米应用,天津,v2.0

2017-08-15,许老师,共享女友,360应用,北京,v2.0
2017-08-15,赵老师,共享女友,安智市场,北京,v1.2
2017-08-15,赵老师,共享女友,安智市场,北京,v1.5

变为一下格式,表示用户今日版本升级的过程

2017-08-14 许老师 共享女友 360应用 天津 v1.0 v1.2
2017-08-14 许老师 共享女友 小米应用 天津 v1.2 v2.0

思路:
将最左的字段版本信息新生成一行相同的但是向下移动一位,然后将左边版本号小于右边的提取出来。可用窗口分析函数实现:lag(app_ver_name,1,null) over(partition by user_id order by app_ver_name)

lag(参数一:要移动的字段,参数二:下移的行数,参数三:空缺的用啥填充) over((partition by 参数四:组内根据什么排序 order by 参数五:按什么分组)
解决方案:

–创建表格,输入数据
create table t_lag_test(day string,user_id string,app_token string,release_channel string,city string,app_ver_name string)
row format delimited fields terminated by ‘,’;

load data local inpath ‘/root/hivetest/ver.test’ into table t_lag_test;

– 数据统计
select
day,user_id,app_token,release_channel,city,ver_2,app_ver_name
from
(
select
day,user_id,app_token,release_channel,city,app_ver_name,
lag(app_ver_name,1,null) over(partition by user_id order by app_ver_name) as ver_2
from t_lag_test) tmp
where ver_2 is not null and app_ver_name>ver_2
;

更多窗口分析函数使用方法,点这里看更多窗口分析函数

将hive上统计好的数据表导出至mysql

将app数据仓库中的 日新用户维度统计报表:dim_user_new_day 导出到mysql的表中去

– 1 在mysql中建库建表

create database app;
create table dim_user_new_day(
os_name varchar(20),city varchar(20),release_channel varchar(20),app_ver_name varchar(20),cnts int,dt varchar(20)
);

–注意:将库和表的编码集改成utf8,命令如下:
修改库的编码:
mysql> alter database db_name character set utf8;
修改表的编码:
mysql> ALTER TABLE table_name CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

– 2 用sqoop将hive中的 dim_user_new_day 中的指定日分区的数据导出到mysql 的

dim_user_new_day
#!/bin/bash
day=`date -d '-1 day' +'%Y-%m-%d'`

/root/apps/sqoop/bin/sqoop export \
--connect "jdbc:mysql://hdp-04:3306/app?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password root \
--input-fields-terminated-by '\001' \
--table dim_user_new_day \
--export-dir /user/hive/warehouse/app.db/dim_user_new_day_1p/day=${day} /

你可能感兴趣的:(hadoop)