Hive多字节分隔符解决方案
Hive多字节分隔符解决方案 目录1 应用场景1.1 Hive中的分隔符1.2 特殊数据2 问题与需求2.1问题2.2 情况二:数据中包含了分隔符3 解决方案3.1 解决方案一:替换分隔符3.2 解决方案二:RegexSerDe正则加载3.3 解决方案三:自定义InputFormat4 总结1 应用场景1.1 Hive中的分隔符Hive中默认使用单字节分隔符来加载文本数据,例如逗号、制表符、空格等等,默认的分隔符为001。根据不同文件的不同分隔符,我们可以通过在创建表时使用 row format delimited fields terminated by 单字节分隔符 来指定文件中的分割符,确保正确将表中的每一列与文件中的每一列实现一一对应的关系。1.2 特殊数据在实际工作中,我们遇到的数据往往不是非常规范化的数据,例如我们会遇到以下的两种情况· 情况一:每一行数据的分隔符是多字节分隔符,例如:”|”、“-”等上图中每列的分隔符为|,为多字节分隔符· 情况二:数据的字段中包含了分隔符上图中每列的分隔符为空格,但是数据中包含了分割符,时间字段中也有空格192.168.88.134 08/Nov/2020:10:44:32 +0800 "GET / HTTP/1.1" 404 9512 问题与需求2.1问题基于上述的两种特殊数据,我们如果使用正常的加载数据的方式将数据加载到表中,就会出以下两种错误:· 情况一:加载数据的分隔符为多字节分隔符创建表-如果表已存在就删除表 drop table if exists singer; -创建表 create table singer( id string,-歌手id name string,-歌手名称 country string,-国家 province string,-省份 gender string,-性别 works string-作品 ) -指定列的分隔符为| row format delimited fields terminated by '|' 加载数据load data local inpath '/export/data/test01.txt' into table singer; 查看结果select * from singer;问题数据发生了错位,没有正确的加载每一列的数据原因Hive中默认只支持单字节分隔符,无法识别多字节分隔符2.2 情况二:数据中包含了分隔符创建表-如果表存在,就删除表 drop table if exists apachelog; -创建表 create table apachelog( ip string, -IP地址 stime string, -时间 mothed string, -请求方式 url string, -请求地址 policy string, -请求协议 stat string, -请求状态 body string -字节大小 ) -指定列的分隔符为空格 row format delimited fields terminated by ' '加载数据load data local inpath '/export/data/apache_web_access.log' into table apachelog查看结果select * from apachelog;问题时间字段被切分成了两个字段,后面所有的字段出现了错位原因时间数据中包含了分隔符,导致Hive认为这是两个字段,但实际业务需求中,为一个字段需求基于上面两种情况的测试发现,当数据中出现了多字节分隔符或者数据中的某个字段包含了分隔符,就会导致数据加载错位的问题。基于出现的问题,我们需要通过特殊的方法来解决该问题,即使当数据中出现多字节分隔符等情况时,Hive也能正确的加载数据,实现列与数据的一一对应。3 解决方案3.1 解决方案一:替换分隔符方案概述面对情况一,如果数据中的分隔符是多字节分隔符,可以使用程序提前将数据中的多字节分隔符替换为单字节分隔符,然后使用Hive加载,就可以实现正确加载对应的数据。例如:原始数据中的分隔符为“|” 程序开发可以在ETL阶段通过一个MapReduce程序,将“|”替换为单字节的分隔符“|”,示例程序如下:import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.ToolRunner; import java.io.IOException; /* * ClassName ChangeSplitCharMR * Description TODO MapReduce实现将多字节分隔符转换为单字节符 * Create By itcast */ public class ChangeSplitCharMR extends Configured implements Tool public int run(String arg) throws Exception /* * 构建Job */ Job job = Job.getInstance(this.getConf(),"changeSplit"); job.setJarByClass(ChangeSplitCharMR.class); /* * 配置Job */ /input:读取需要转换的文件 job.setInputFormatClass(TextInputFormat.class); Path inputPath = new Path("datas/split/test01.txt"); FileInputFormat.setInputPaths(job,inputPath); /map:调用Mapper job.setMapperClass(ChangeSplitMapper.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(NullWritable.class); /reduce:不需要Reduce过程 job.setNumReduceTasks(0); /output job.setOutputFormatClass(TextOutputFormat.class); Path outputPath = new Path("datas/output/changeSplit"); TextOutputFormat.setOutputPath(job,outputPath); /* * 提交Job */ return job.waitForCompletion(true) ? 0 : -1; /程序入口 public static void main(String args) throws Exception /调用run Configuration conf = new Configuration(); int status = ToolRunner.run(conf, new ChangeSplitCharMR(), args); System.exit(status); public static class ChangeSplitMapper extends Mapper<LongWritable,Text,Text,NullWritable> /定义输出的Key private Text outputKey = new Text(); /定义输出的Value private NullWritable outputValue = NullWritable.get(); Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException /获取每条数据 String line = value.toString(); /将里面的|转换为| String newLine = line.replaceAll("|", "|"); /替换后的内容作为Key this.outputKey.set(newLine); /输出结果 context.write(this.outputKey,this.outputValue); 程序执行结果如下: 重新建表加载数据1. 重新创建Hive表-如果表已存在就删除表drop table if exists singer;-创建表create table singer( id string,-歌手id name string,-歌手名称 country string,-国家 province string,-省份 gender string,-性别 works string-作品)-指定列的分隔符为|row format delimited fields terminated by '|'在Hive中重新加载数据load data local inpath '/export/data/part-m-00000' into table singer;查看结果 总结在ETL阶段可以直接对数据进行分隔符的替换,通过替换分隔符将多字节分隔符更改为单字节分隔符,就可以解决数据加载的问题,但是这种方式有对应的优缺点,并不是所有的场景适用于该方法。优点:实现方式较为简单,基于字符串替换即可缺点:无法满足情况2的需求 3.2 解决方案二:RegexSerDe正则加载· 1 方案概述面对情况一和情况二的问题,Hive中提供了一种特殊的方式来解决,Hive提供了一种特殊的Serde来加载特殊数据的问题,使用正则匹配来加载数据,匹配每一列的数据。官网地址:https:/cwiki.apache.org/conflu