Spark OLAP on Nested Structure Data

本文介绍了使用Spark SQL分析处理嵌套结构数据的方法,包括将protobuf格式数据转换为DataFrame,字段裁剪优化,自动展平嵌套结构,以及通过解析SQL获取数据源和时间。通过这些技术,可以更高效地对原始日志数据进行分析和处理。
摘要由CSDN通过智能技术生成

引言

大多数系统中,采集的原始日志数据往往都是半结构化或者非结构化的。而对于数据分析人员来说,每次写代码去处理原始数据不够高效且有一定的难度,二维表结构的数据使用起来才是最自然方便的。因此,数据处理的第一步往往是将原始数据展开成一张底层大宽表以供上层的数据应用或者数据开发人员使用。但对于原始日志数据中字段往往很多,而且多是嵌套结构的,对于底层大宽表应该包含哪些字段,以及对于嵌套结构如何处理,这是一个令人头疼的问题。

那能直接使用SQL方法分析处理原始数据吗?

一方面,Spark计算框架可以支持大数据量的计算,而且其支持SQL接口;另一方面,Protobuf是一种常用的数据接口协议。本文将protobuf协议为例介绍使用Spark SQL分析处理原始数据的方案。

对于上层报表类应用来说,我们可以基于报表需求选取字段形成大宽表。但对于分析型应用,特别对于一些问题诊断分析场景来说,往往需要一些不常使用的字段,且往往需要最原始未经加工的数据。

对于其它嵌套结构数据,如json,只需要将RDD转换DataFrame步骤进行替换即可达到同样的目的。

将原始数据转换为DataFrame

原始数据是protobuf协议的数据,如果能将protobuf格式的日志数据转换成DataFrame,那就可以使用Spark SQL来进行数据分析计算了。

定义数据格式

假设protobuf协议如下:

package test;

enum Sex {
    Female = 1;
    Male = 2;
}

message Job {
    required string company = 1;
    repeated string industries = 2;
}

message Person {
    required string name = 1;
    required Sex sex = 2;
    required int32 age = 3;
    repeated string interests = 4;
    repeated Job jobs = 5;
}

message Log {
    repeated Person person = 1;
}

读取数据为RDD

假设日志数据已经采集好并存储在hdfs或其他存储系统中,那么首先可通过相应读数据方法加载数据并转换为RDD[Protobuf]格式。下面以本地的测试数据person.txt为例进行介绍:

person {
  name: "Li"
  sex: Male
  age: 27
  interests: "badminton"
  interests: "running"
  interests: "reading"
  jobs {
    company: "Google"
    industries: "Internet"
  }
}
person {
  name: "Zhang"
  sex: Male
  age: 26
  interests: "basketball"
  interests: "running"
  interests: "reading"
  jobs {
    company: "Tencent"
    industries: "Internet"
    industries: "Game"
  }
}

person {
   name: "Wang"
   sex: Female
   age: 26
   interests: "dancing"
   interests: "swim"
   interests: "reading"
   jobs {
     company: "Alibaba"
     industries: "Internet"
     industries: "E-commerce"
   }
 }

person {
   name: "Liu"
   sex: Female
   age: 23
   interests: "song"
   interests: "swim"
   interests: "game"
   jobs {
     company: "Tencent"
     industries: "Internet"
     industries: "Game"
   }
}

读取本地测试数据,并将其转换为RDD[Person]:

val log = {
  val in = getClass.getResourceAsStream("/person.txt")
  val builder = Log.newBuilder()
  TextFormat.merge(new BufferedReader(new InputStreamReader(in)), builder)

  builder.build()
}
val personRdd = sc.parallelize(log.getPersonList)

将RDD[Protobuf]转换为DataFrame

如果是RDD[JavaBean]格式的数据,可以利用SparkSession中createDataFrame方法将其转换为DataFrame。那对于Protobuf格式的数据也可通过自动解析Protobuf协议的方式进行转换,可参考saurfang的sparksql-protobuf包(也可以参考其源码编写自己的转换方式)将RDD[Protobuf]转换为DataFrame。其依赖maven包如下:

<!-- https://mvnrepository.com/artifact/com.github.saurfang/sparksql-protobuf -->
<dependency>
    <groupId>com.github.saurfang</groupId>
    <artifactId>sparksql-protobuf_2.11</artifactId>
    <version>0.1.3</version>
</dependency>

将RDD[Person]转换为DataFrame的schema如下:

import com.github.saurfang.parquet.proto.spark.sql._
val personsDF = sparkSession.sqlContext.createDataFrame(personRdd)
personsDF.printSchema()

root
 |-- name: string (nullable = false)
 |-- sex: string (nullable = false)
 |-- age: integer (nullable = false)
 |-- interests: array (nullable = false)
 |    |-- element: string (containsNull = false)
 |-- jobs: array (nullable = false)
 |    |-- element: struct (containsNull = false)
 |    |    |-- company: string (nullable = false)
 |    |    |-- industries: array (nullable = false)
 |    |    |    |-- element: string (containsNull = false)

转换为DataFrame结构的数据后,就可以使用Spark SQL对其进行分析处理。比如可统计不同性别下的人数:

personsDF.createOrReplaceTempView("person")
val result =
  sparkSession.sql("select sex, count(*) as cnt from person group by sex")
result.show()

+------+---+
|   sex|cnt|
+------+---+
|Female|  2|
|  Male|  2|
+------+---+

字段裁剪优化

在将RDD[Protobuf]转换为DataFrame时,数据占用的内存可能会膨胀很多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值