get json object hive 解析MongoDB json嵌套 文档数据存入hive后做字段取值

Hive是大数据生态圈的数据仓库工具。通过hive,我们可以方便地进行ETL的工作。我们之前的文章已经介绍过一些hive sql的基本操作以及具体的实例代码演示,今天我们介绍另一个场景应用,就是在hive中解析json嵌套。

一般情况下,存储json文本格式的工具是MongoDB这些nosql db, 而hive和MongoDB是可以相互做数据的传输同步的,假设从MongoDB中把数据同步到hive中,我们在hive里对数据做解析,取值的基本操作如下。

1、存入方式

MongoDB的文档数据存入hive时,尽可能把整个文档存入到一个hive表字段中(如果有业务限定或者有特定场景需要把个别字段单独提取出来除外)。

2、json示例

json数据格式
{
    "bedroom": {
        "min_value": 2,
        "max_value": 3,
        "tags": [
            {
                "id": 2,
                "name": "2居室",
                "score": 0.00055233
            },
            {
                "id": 3,
                "name": "3居室",
                "score": 0.00055233
            }
        ]
    },
    "build_type": [
        {
            "id": 1,
            "name": "板楼",
            "score": 0.00055233
        }
    ],
    "user_level": 1,
    "user_type": 2
}

这个json数据中有不同的类型的json嵌套,这一个document我们会整体存入hive的一个字段中,比如字段名我们叫做 content;而我们在hive中对json数据做解析一般会用到两个函数,分别是get_json_object和json_tuple, 这两个函数都是对json字段做解析,区别是json_tuple是可以一次提取多个json中的字段,而 get_json_object 只能提取一个字段。

直接提取普通字段或者不做深层解析

select
      get_json_object(content, '$.user_level') as level
from document

对list格式的嵌套json做解析

select pp.col,r.name, r.score
from (
      select split(regexp_replace(regexp_extract(content,
                        '^\\[(.+)\\]$',1),
             '\\}\\,\\{', '\\}\\|\\|\\{'),
       '\\|\\|'
     ) as cont
from document) ss
lateral view explode(ss.cont) pp as col 
lateral view json_tuple(ss.cont,'name','score') r as name,score;

apache hive 查询优化 group by distinct join

我们在之前的文章中介绍了hive的sort by , distribute by 等操作和相关使用技巧,除了之前说的这些常用函数外,我们在查询的时候还会用到分组、去重、关联等操作。

一、join优化

Join查找操作的基本原则:应该将条目少的表/子查询放在 Join 操作符的左边。
原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生内存溢出错误的几率。

1、Join查找操作中如果存在多个join,且所有参与join的表中其参与join的key都相同,则会将所有的join合并到一个mapred程序中。

hive > SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)  
# 在一个mapre程序中执行join
hive > SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)   
# 在两个mapred程序中执行join

2、Map join的关键在于join操作中的某个表的数据量很小。
Mapjoin 的限制是无法执行a FULL/RIGHT OUTER JOIN b,和map join相关的hive参数:hive.join.emit.interval  hive.mapjoin.size.key  hive.mapjoin.cache.numrows

hive> SELECT /*+ MAPJOIN(b) */ a.key, a.value
  FROM a join b on a.key = b.key

3、由于join操作是在where操作之前执行,所以当你在执行join时,where条件并不能起到减少join数据的作用

hive > SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
  WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'
hive > SELECT a.val, b.val FROM a LEFT OUTER JOIN b
  ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')
# 优化修改后的where条件

4、在join操作的每一个mapred程序中,hive都会把出现在join语句中相对靠后的表的数据stream化,相对靠前的变的数据缓存在内存中。当然,也可以手动指定stream化的表。

hive > SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)

二、group by 优化

Map端聚合,首先在map端进行初步聚合,最后在reduce端得出最终结果,相关参数:· hive.map.aggr = true是否在 Map 端进行聚合,默认为 True· hive.groupby.mapaggr.checkinterval = 100000在 Map 端进行聚合操作的条目数目数据倾斜聚合优化,设置参数hive.groupby.skewindata = true,当选项设定为 true,生成的查询计划会有两个 MR Job。

第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;

第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

三、Hive实现(not) in

通过left outer join进行查询,(假设B表中包含另外的一个字段 key1

hive > select a.key from a left outer join b on a.key=b.key where b.key1 is null

通过left semi join 实现 in;Left semi join 的限制:join条件中右边的表只能出现在join条件中。

hive> SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key) 

四、合并小文件

文件数目过多,会给 HDFS 带来压力,并且会影响处理效率,可以通过合并 Map 和 Reduce 的结果文件来消除这样的影响:

  • hive.merge.mapfiles = true是否和并 Map 输出文件,默认为 True·
  • hive.merge.mapredfiles = false是否合并 Reduce 输出文件,默认为 False·
  • hive.merge.size.per.task = 256*1000*1000合并文件的大小

五、使用分区

Hive中的每个分区都对应hdfs上的一个目录,分区列也不是表中的一个实际的字段,而是一个或者多个伪列,在表的数据文件中实际上并不保存分区列的信息与数据。

Partition关键字中排在前面的为主分区(只有一个),后面的为副分区静态分区:

静态分区在加载数据和使用时都需要在sql语句中指定         

案例:(stat_date=’20120625′,province=’hunan’)

动态分区:使用动态分区需要设置hive.exec.dynamic.partition参数值为true,默认值为false,在默认情况下,hive会假设主分区时静态分区,副分区使用动态分区;如果想都使用动态分区,需要设置set hive.exec.dynamic.partition.mode=nostrick,默认为strick         

案例:(stat_date=’20120625′,province)

六、Distinct 使用

Hive支持在group by时对同一列进行多次distinct操作,却不支持在同一个语句中对多个列进行distinct操作。

apache hive 函数row_number(),rank(),dense_rank()的应用介绍

场景:hsql(hive sql)处理数据的时候会有取分组后topN的需求,这个时候们可能会用到同一检索条件下的组内排序,涉及到的函数有row_number(),rank(),dense_rank()

  • row_number:不管排名是否有相同的,都按照顺序1,2,3…..n
  • rank:排名相同的名次一样,同一排名有几个,后面排名就会跳过几次
  • dense_rank:排名相同的名次一样,且后面名次不跳跃

数据准备

# province, city, id
浙江,杭州,300
浙江,宁波,150
浙江,温州,200
浙江,嘉兴,100
江苏,南京,270
江苏,苏州,299
江苏,某市,200
江苏,某某市,100
top n 样例数据

普通排序

hive > select * from hive_table order by id desc;
浙江    杭州    300
浙江    宁波    150
浙江    温州    200
浙江    嘉兴    100
江苏    南京    270
江苏    苏州    299
江苏    某市    200
江苏    某某市    100

综合查询排序

hive > select province,city,
rank() over (order by id desc) rank,
dense_rank() over (order by id desc) dense_rank,
row_number() over(order by id desc) row_number
from hive_table
group by province,city,people;
浙江    杭州    300    1    1    1
江苏    苏州    299    2    2    2
江苏    南京    270    3    3    3
江苏    某市    200    4    4    4
浙江    温州    200    4    4    5
浙江    宁波    150    6    5    6
江苏    某某市    100    7    6    7
浙江    嘉兴    100    7    6    8

主要说明讲解如下:

  • row_number顺序下来, 顺序下来的序号不会有重复的
  • rank:在遇到数据相同项时,会留下空位,(红框内第一列,4,4,6) ,相同的会占一个位置,接下来的数字变成 +2
  • dense_rank:在遇到数据相同项时,不会留下空位,(红框内第一列,4,4,5) 相同的是同一个序号,但是接下来的还是 +1

分区统计查询排序

hive > select province,city,
rank() over (partition by province order by id desc) rank,
dense_rank() over (partition by province order by id desc) dense_rank,
row_number() over(partition by province order by id desc) row_number
from hive_table
group by province,city,people;
江苏    苏州    299    1    1    1
江苏    南京    270    2    2    2
江苏    某市    200    3    3    3
江苏    某某市    100    4    4    4
浙江    杭州    300    1    1    1
浙江    温州    200    2    2    2
浙江    宁波    150    3    3    3
浙江    嘉兴    100    4    4    4