天天看点

关于hive RegexSerDe的源码分析

  最近有个业务建表使用了 RegexSerDe,之前虽然也它来解析nginx日志,但是没有做深入的了解。这次看了下其实现方式。

建表语句:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code>CREATE external TABLE ods_cart_log</code>

<code>(</code>

<code>time_local STRING,</code>

<code>request_json  STRING,</code>

<code>trace_id_num STRING</code>

<code>)</code>

<code>PARTITIONED BY</code>

<code>dt string,</code>

<code>hour string</code>

<code>ROW FORMAT SERDE </code><code>'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'</code>

<code>WITH SERDEPROPERTIES</code>

<code>(</code><code>"input.regex"</code> <code>=</code>

<code>"\\\[(.*?)\\\] .*\\\|(.*?) (.*?) \\\[(.*?)\\\]"</code><code>,</code>

<code>"output.format.string"</code> <code>=</code><code>"%1$s %2$s  %4$s"</code><code>)</code>

<code>STORED AS TEXTFILE;</code>

测试数据:

<code>[2014-07-24 15:54:54] [6] OperationData.php: </code>

<code>:89|{</code><code>"action"</code><code>:</code><code>"add"</code><code>,</code><code>"redis_key_hash"</code><code>:9,</code><code>"time"</code><code>:</code><code>"1406188494.73745500"</code><code>,</code><code>"source"</code><code>:</code><code>"web"</code><code>,</code>

<code>"mars_cid"</code><code>:</code><code>""</code><code>,</code><code>"session_id"</code><code>:</code><code>""</code><code>,</code><code>"info"</code><code>:{</code><code>"cart_id"</code><code>:26885,</code><code>"user_id"</code><code>:4,</code><code>"size_id"</code><code>:</code><code>"2784145"</code><code>,</code>

<code>"num"</code><code>:</code><code>"1"</code><code>,</code><code>"warehouse"</code><code>:</code><code>"VIP_NH"</code><code>,</code><code>"brand_id"</code><code>:</code><code>"7379"</code><code>,</code><code>"cart_record_id"</code><code>:26885,</code><code>"channel"</code><code>:</code><code>"te"</code><code>}}</code>

<code> </code><code>trace_id [40618849399972881308]</code>

这里trace_id_num按照猜想应该是第4个字段(即40618849399972881308),但是实际输出了第3个字段(trace_id)

查看其代码实现:

RegexSerDe主要由下面三个参数:

1)input.regex 正则

2)output.format.string 输出格式

3)input.regex.case.insensitive  大小写是否敏感

其中input.regex用在反序列化方法中,即数据的读取(hive读取hdfs文件),相对的output.format.string 用在序列化的方法中,即数据的写入(hive写入hdfs文件)。

在反序列化的方法deserialize中有如下代码,用于返回代表匹配字段的数据:

<code>   </code><code>for</code> <code>(</code><code>int</code> <code>c = </code><code>0</code><code>; c &lt; numColumns; c++) {   </code><code>//numColumns是按表中column的数量算的(</code>

<code>   </code><code>比如这个例子columnNames 是[time_local, request_json, trace_id_num]   | numColumns = columnNames.size();</code>

<code>      </code><code>try</code> <code>{</code>

<code>        </code><code>row.set(c, m.group(c + </code><code>1</code><code>));  </code><code>//可以看到字段的匹配从0开始,中间不会有跳跃,</code>

<code>        </code><code>所以这里select  trace_id_num 字段是正则里面的第</code><code>3</code><code>个组,而和output.format.string没有关系</code>

<code>          </code><code>} </code><code>catch</code> <code>(RuntimeException e) {</code>

<code>        </code><code>partialMatchedRows++;</code>

<code>        </code><code>if</code> <code>(partialMatchedRows &gt;= nextPartialMatchedRows) {</code>

<code>          </code><code>nextPartialMatchedRows = getNextNumberToDisplay(nextPartialMatchedRows);</code>

<code>          </code><code>// Report the row</code>

<code>          </code><code>LOG.warn(</code><code>""</code> <code>+ partialMatchedRows</code>

<code>              </code><code>+ </code><code>" partially unmatched rows are found, "</code> <code>+ </code><code>" cannot find group "</code>

<code>              </code><code>+ c + </code><code>": "</code> <code>+ rowText);</code>

<code>        </code><code>}</code>

<code>        </code><code>row.set(c, </code><code>null</code><code>);</code>

<code>      </code><code>}</code>

<code>    </code><code>}</code>

  work around的方法有两个,1个是把所有正则匹配的字段列出,另一个就是更改正则的分组,只拿自己care的分组,比如上面可以改为

<code>\\\[(.*?)\\\] .*\\\|(.*?) .*? \\\[(.*?)\\\]</code>

  这里output.format.string的设置仔细想想貌似没什么用,首先RegexSerDe的方式只在textfile下生效,即可以用load向hive的表中导入数据,但是load是一个hdfs层面的文件操作,不涉及到序列化,如果想使用序列化,需要使用insert into select的方式插入数据,但是这种方式插入的数据又和select的数据有关系,和output.format.string没什么关系了。。

其实regexserde类有两个

分别位于

<code>./serde/src/java/org/apache/hadoop/hive/serde2/RegexSerDe.java 和</code>

<code>./contrib/src/java/org/apache/hadoop/hive/contrib/serde2/RegexSerDe.java</code>

都是扩展了AbstractSerDe这个抽象类。通过代码可以看到contrib下的这个类是实现了serialize 和 deserialize 方法,而上面这个只实现了deserialize 方法,由此看来RegexSerDe中的serialize 方法可能是没什么用的。。

另外需要注意几点:

1.如果一行匹配不上,整个行的字段输出都是null

<code> </code><code>if</code> <code>(!m.matches()) {</code>

<code>      </code><code>unmatchedRows++;</code>

<code>      </code><code>if</code> <code>(unmatchedRows &gt;= nextUnmatchedRows) {</code>

<code>        </code><code>nextUnmatchedRows = getNextNumberToDisplay(nextUnmatchedRows);</code>

<code>        </code><code>// Report the row</code>

<code>        </code><code>LOG.warn(</code><code>""</code> <code>+ unmatchedRows + </code><code>" unmatched rows are found: "</code> <code>+ rowText);</code>

<code>      </code><code>return</code> <code>null</code><code>;</code>

2.表的字段类型必须都是string,否则会报错,如果需要别的字段,可以在select中使用cast做转换

<code>    </code><code>for</code> <code>( </code><code>int</code> <code>c = </code><code>0</code><code>; c &lt; numColumns ; c++) {</code>

<code>      </code><code>if</code> <code>(!columnTypes.get(c).equals( TypeInfoFactory.stringTypeInfo)) {</code>

<code>        </code><code>throw</code> <code>new</code> <code>SerDeException(getClass().getName()</code>

<code>            </code><code>+ </code><code>" only accepts string columns, but column["</code> <code>+ c + </code><code>"] named "</code>

<code>            </code><code>+ columnNames.get(c) + </code><code>" has type "</code> <code>+ columnTypes.get(c));</code>

本文转自菜菜光 51CTO博客,原文链接:http://blog.51cto.com/caiguangguang/1532987,如需转载请自行联系原作者