文章目录
- 概述
- 1. 引出需要进行数据预处理的必要性[→](#toc)
- 2. 使用RegexSerDe处理apache或者ngnix日志文件[→](#toc)
- 3. 根据不同业务拆表[→](#toc)
-
- 4. 数据清洗[→](#toc)
- 4.1 Hive自定义函数的方式
- 4.2 UDF去除数据双引号
- 4.3 UDF转换日期时间格式
- 5. 编写hql分许数据[→](#toc)
- 5.1 分析用户访问网站的时间段
- 5.2 分析用户的ip地址
- 总结
概述
本文将基于Hive数据仓库工具对一份网站日志进行数据分析,包括分析IP地址。包括在插入数据时使用正则表达式对日志文件进行预处理、利用UDF进行数据清洗、使用ORC格式存储和SNAPPY压缩等。
1. 引出需要进行数据预处理的必要性→
-
原日志文件的字段信息统计如下,总共11个字段:
-
日志文件中信息展示:
"27.38.5.159"
"-"
"31/Aug/2015:00:04:37 +0800"
"GET /course/view.php?id=27 HTTP/1.1"
"303"
"440"
-
"http://www.ibeifeng.com/user.php?act=mycourse"
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36"
"-"
"learn.ibeifeng.com"
-
正常创建表的操作
// 建表,以空格划分字段
create table IF NOT EXISTS default.bf_log_src (
remote_addr string,
remote_user string,
time_local string,
request string,
status string,
body_bytes_sent string,
request_body string,
http_referer string,
http_user_agent string,
http_x_forwarded_for string,
host string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ' '
stored as textfile ;
// 载入数据
load data local inpath '/opt/datas/moodle.ibeifeng.access.log' into table bf_log_src ;
// 查看表结构
desc formatted bf_log_src;
// 查询行数
select count(*) from bf_log_src ;
// 查看前5行
select * from bf_log_src limit 5 ;
-
发现问题
通过select * from bf_log_src limit 5 ;
之后会发现,表里并没有正常显示日志文件里的11个字段的值,而是只有前面8 个字段,后面字段丢失了。
仔细观察日志文件信息,可以发现有些字段中本身存在空格
-
解决问题
推荐的解决问题方式是:利用正则表示式过滤。当然,数据预处理也可以借助Python脚本,可以参照基于Python预处理、用Hive对movielens数据集进行分析
2. 使用RegexSerDe处理apache或者ngnix日志文件→
- Apache官网对日志文件的处理示例(示例中的正则表达是有误的)
CREATE TABLE apachelog (
host STRING,
identity STRING,
user STRING,
time STRING,
request STRING,
status STRING,
size STRING,
referer STRING,
agent STRING)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
"input.regex" = "([^]*) ([^]*) ([^]*) (-|\\[^\\]*\\]) ([^ \"]*|\"[^\"]*\") (-|[0-9]*) (-|[0-9]*)(?: ([^ \"]*|\".*\") ([^ \"]*|\".*\"))?"
)
STORED AS TEXTFILE;
- 只要套用Apache官网的示例,修改正则表达式为自己适用的即可。
- 本项目的正确建表姿势
drop table if exists default.bf_log_src ;
create table IF NOT EXISTS default.bf_log_src (
remote_addr string,
remote_user string,
time_local string,
request string,
status string,
body_bytes_sent string,
request_body string,
http_referer string,
http_user_agent string,
http_x_forwarded_for string,
host string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
"input.regex" = "(\"[^ ]*\") (\"-|[^ ]*\") (\"[^\]]*\") (\"[^\"]*\") (\"[0-9]*\") (\"[0-9]*\") (-|[^ ]*) (\"[^ ]*\") (\"[^\"]*\") (-|[^ ]*) (\"[^ ]*\")"
)
STORED AS TEXTFILE;
load data local inpath '/opt/datas/moodle.ibeifeng.access.log' into table default.bf_log_src ;
- 上文使用到的正则表达式语法
小括号内看成一个整体
\代表转义
|代表或
^代表取反
*代表匹配多个
.*代表匹配所有的
[0-9]代表单个数字
[0-9]*代表0-9之间的多个字符
3. 根据不同业务拆表→
3.1 需求分析
- ip地址
- 依据ip地址确定区域,定向营销
- 用户统计,访问某一网站数
- 访问时间
- 分析用户访问网站的时间段
- 针对销售来说,合理安排值班
- 请求地址
- 转入链接
- 关注用户如何访问我们的产品
- 定向某个区域,进行广告投放
3.2 拆表
drop table if exists default.bf_log_comm ;
create table IF NOT EXISTS default.bf_log_comm (
remote_addr string,
time_local string,
request string,
http_referer string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc tblproperties ("orc.compress"="SNAPPY");
# 最后一行:设置orc存储格式(orc的特点是存储空间小),指定snappy压缩
# 子查询插入数据
insert into table default.bf_log_comm select remote_addr, time_local, request,http_referer from default.bf_log_src ;
# 查询前5条记录
select * from bf_log_comm limit 5 ;
4. 数据清洗→
4.1 Hive自定义函数的方式
- maven项目下添加依赖:
hadoop-client、hive-exec、hive-jdbc
- 继承UDF类:
import org.apache.hadoop.hive.ql.exec.UDF;
- 实现至少一个evaluate方法,evaluate方法支持重载,并且返回值不允许为void
- 写完程序,打jar包上传到Linux系统中(linux有IDE的话,直接在linux上操作)。截图加文字,详细记录IDEA导出jar包的方式
- 与jar包进行关联:
add jar /opt/datas/udf.jar;
- 创建function函数方法:
create temporary function my_udf as '包名.BigDataUdf';
- SQL里调用该自定义函数
4.2 UDF去除数据双引号
- show functions; 查看hive中的函数,发现没有适合的去除引号的函数。其实有,但是需要调用不止一个函数进行处理,这样会降低性能,UDF更好,所以说,合理使用UDF也是Hive调优的方式。企业里常常会为Hive定义成千上百个UDF。
- 综上,通过自定义函数的方式来解决这个问题
- UDF代码如下:
package com.bigdata.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
/**
* 1. Implement one or more methods named "evaluate" which will be called by Hive.
* 2. "evaluate" should never be a void method. However it can return "null" if needed.
*
*/
public class RemoveQuotesUDF extends UDF {
public Text evaluate(Text str){
// validate
if(null == str){
return null ;
}
if(null == str.toString()){
return null ;
}
// remove
return new Text (str.toString().replaceAll("\"", "")) ;
}
public static void main(String[] args) {
System.out.println(new RemoveQuotesUDF().evaluate(new Text("\"12\"")));
}
}
- 使用UDF,以覆盖的方式插入数据
# 添加jar
add jar /opt/datas/jar/hiveUDF.jar ;
# 创建自定义函数
create temporary function my_removequotes as "com.bigdata.hive.udf.RemoveQuotesUDF" ;
# 查看有多少jar
list jars;
insert overwrite table default.bf_log_comm select my_removequotes(remote_addr), my_removequotes(time_local), my_removequotes(request), my_removequotes(http_referer) from default.bf_log_src ;
select * from bf_log_comm limit 5 ;
4.3 UDF转换日期时间格式
- 如4.2的模式,编写完程序并上传到linux,add到Hive,create temporary function。
- 时间转换函数代码:
package com.bigdata.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DateTransformUDF extends UDF {
// set date format for input and output
private final SimpleDateFormat inputFormat = new SimpleDateFormat("dd/MMM/yyyy:hh:mm:ss",Locale.ENGLISH);
private final SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMddHHmmss");
public Text evaluate(Text input){
Text output = new Text();
if(null == input)
return null;
String inputDate = input.toString().trim();
if(inputDate.equals(""))
return null;
try {
// parse
Date parseDate = inputFormat.parse(inputDate);
// date transform(set format)
String outputDate = outputFormat.format(parseDate);
// String to Text
output.set(outputDate);
} catch (ParseException e) {
e.printStackTrace();
return output;
}
return output;
}
public static void main(String[] args) {
System.out.println(new DateTransformUDF().evaluate(new Text("31/Aug/2015:00:04:37 +0800")));
}
}
- 覆盖数据
insert overwrite table default.bf_log_comm select my_removequotes(remote_addr), my_datetransform(my_removequotes(time_local)), my_removequotes(request), my_removequotes(http_referer) from default.bf_log_src ;
select * from bf_log_comm limit 5 ;
5. 编写hql分许数据→
5.1 分析用户访问网站的时间段
- 当前时间字段time_local的值是yyyyMMddHHmmss格式
- 分析用户访问网站的时间段只需要获取HH(小时)即可
- 使用Hive提供的函数截取time_local字符串:substring(小标从1开始)
- 查看函数详细使用方法:
desc function extended substring;
- hql:
select hour, count(hour) cnt from
(select substring(time_local, 9, 2) hour from bf_log_comm) t
group by hour order by cnt desc;
- 结果分析
用户一般在下午3点到5点访问网站。
5.2 分析用户的ip地址
-
只需要根据ip地址的前2段即可获知地域信息,因此只需要查询ip字段的前两段
-
在国内,前两段最多7位,最少5位,所以可以用substring(remote_addr,1,7)截取字段。当然,也可以用UDF,这里使用UDF。
-
UDF代码如下:
public Text evaluate(Text input){
// verify
if(null == input || null == input.toString()) return null;
Text output = new Text();
// split by "."
String[] inputSplit = input.toString().trim().split("\\.");
// split join
String outputStr = inputSplit[0]+"."+inputSplit[1];
// set output
output.set(outputStr);
return output;
}
-
hql如下
add jar /opt/datas/hiveUDF.jar
create temporary function my_getRegion as 'com.bigdata.hive.udf.GetRegionUDF';
select t.addr, count(addr) cnt from
(
select my_getRegion(remote_addr) addr from bf_log_comm
) t
group by t.addr order by cnt desc limit 12;
- ip对应的地理位置可以放在一张表里,和查询结果join一下–》小表对大表:map join
总结
指对两个字段进行分析,其余两个字段的分析是类似的。学过spark就知道,以上的分析在spark里,只要一行就够了。不过spark只能代替作为查询引擎,却不能代替hive作为大数据仓库工具本身,因此,有必要认真学习。我的博客里有另一篇关于Hive实战的文章,里边的操作和分析会相对复杂些,也会用到sqoop、mysql等。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)