spark中通过rdd、dataframe和spark sql实现相同sql运行速度对比(实测)

决定做一个非常无聊的实验,众所周知现在使用spark进行数据分析一般采用rdd分布式编程、dataframe接口和使用spark sql执行的方式,那么在忽略数据加载速度的情况下,究竟哪种方式的运行速度最快呢?

至于rdd和dataframe数据集的原理和区别,我就不在这里介绍了,可以看RDD DataFrame Dataset 三者的优缺点 , 三者之间的创建 , 以及相互转换这篇文章。

数据集介绍

本文使用的数据集是某省市某网约车公司的交通出行大数据集,由于发布方要求,笔者不会传播该数据集。感兴趣的同学可以前往海口市-交通流量时空演变特征可视分析自行查看和下载数据集。

该数据集包含某平台2017年5月1日到10月31日在某市每天的订单数据,包含订单的起终点经纬度以及订单的类型、出行品类、乘车人数等订单属性数据。

数据加载

首先加载本地数据文件,由于我们是测试,当然是采用local-thread模式运行spark应用。

val path = "data/dwv_order_make_1.txt"  // Current fold file
val textRdd = sc.textFile(path,2)

通过schema,Row构造dataframe

val structFields = Array(StructField("order_id", StringType, nullable = false), // 0
      StructField("product_id", IntegerType, nullable = true),// 1
      StructField("city_id", StringType, nullable = true),// 2
      StructField("district", StringType, nullable = true),// 3
      StructField("county", StringType, nullable = true),// 4
      StructField("type", IntegerType, nullable = true),// 5
      StructField("combo_type", IntegerType, nullable = true),// 6
      StructField("traffic_type", IntegerType, nullable = true),// 7
      StructField("passenger_count", IntegerType, nullable = true),// 8
      StructField("driver_product_id", StringType, nullable = true),// 9
      StructField("start_dest_distance", FloatType, nullable = true),// 10
      StructField("arrive_time", StringType, nullable = true),// 11
      StructField("departure_time", StringType, nullable = true),// 12
      StructField("others", StringType, nullable = true)// 13
    )
    val structType = StructType(structFields) // create schema struct
    val orderRdd = textRdd.map(_.split("\t")).map(x=>Row(x(0), x(1).toInt, x(2), x(3), x(4), x(5).toInt,
      x(6).toInt, x(7).toInt, x(8).toInt, x(9), x(10).toFloat, x(11), x(12), x(13)))
    val df = spark.createDataFrame(orderRdd, structType)
    df.show

这段代码的运行结果如下:

+--------------+----------+-------+--------+------+----+----------+------------+---------------+-----------------+-------------------+-------------------+-------------------+------+
|      order_id|product_id|city_id|district|county|type|combo_type|traffic_type|passenger_count|driver_product_id|start_dest_distance|        arrive_time|     departure_time|others|
+--------------+----------+-------+--------+------+----+----------+------------+---------------+-----------------+-------------------+-------------------+-------------------+------+
|17592725309572|         3|     83|    0898|460107|   0|         0|           0|              0|                3|             5770.0|2017-05-19 11:04:02|2017-05-19 11:01:35|    16|
|17592725855561|         3|     83|    0898|460106|   0|         0|           0|              0|                3|             3839.0|2017-05-19 11:35:04|2017-05-19 11:31:29|    11|
|17592726010947|         3|     83|    0898|460106|   0|         0|           0|              0|                3|             6780.0|2017-05-19 11:51:08|2017-05-19 11:47:13|    17|
+--------------+----------+-------+--------+------+----+----------+------------+---------------+-----------------+-------------------+-------------------+-------------------+------+

spark sql 运行速度测试

我选择了几种常用的sql语句以提高计算复杂度,更贴合业务情况。

我们的sql执行效果是选取某两分钟内,城市编号为’460107’的拼车(或无订单状态)并按照初始上车距离进行排序的订单号。

编写sql语句如下:

SELECT orders.order_id FROM orders WHERE orders.departure_time >= '2017-05-19 17:41:00' AND 
      orders.departure_time <= '2017-05-19 17:43:00' AND orders.county = '460107' AND orders.traffic_type = 0 OR
      orders.traffic_type = 4 
      ORDER BY orders.start_dest_distance

运行结果如下(只保留前五条):

+--------------+
|      order_id|
+--------------+
|17592733902055|
|17592733986620|
|17592733839582|
|17592734004880|
|17592733964943|

使用spark sql 解释并执行该sql语句,共用时9610毫秒。

dataframe 运行速度测试

Spark SQL中的DataFrame类似于一张关系型数据表。在关系型数据库中对单表或进行的查询操作,在DataFrame中都可以通过调用其API接口来实现。

调用代码如下:

df.where("departure_time >='2017-05-19 17:41:00'")
      .where("departure_time <= '2017-05-19 17:43:00'")
      .where("county = '460107'")
      .where("traffic_type = 0 OR traffic_type = 4")
      .orderBy("start_dest_distance")
      .select("order_id")
      .show(1000)

运行结果如下(只保留前五条):

+--------------+
|      order_id|
+--------------+
|17592733902055|
|17592733986620|
|17592733839582|
|17592734004880|
|17592733964943|

可以看到运行结果与spark sql是一致的,使用dataframe提供的api接口共用时8771毫秒。

RDD运行速度测试

下面是一段我自己写的RDD Api编程实现,通过构建一个元组类型的RDD在上面进行过滤、排序等操作。

    val tRDD = textRdd.map(_.split("\t"))
    val f1RDD = tRDD.map(x => (x(12), x(4), x(7).toInt, x(10).toFloat, x(0)))
    val rRDD = f1RDD.filter(_._1 >= "2017-05-19 17:41:00").filter(_._1 <= "2017-05-19 17:43:00")
      .filter(_._2 == "460107").filter(x => x._3 == 4 || x._3 == 0).sortBy(_._4).map(_._5)
    rRDD.collect().foreach {println}

运行结果如下(只保留前五条),可以看出运行结果与前面两种是一致的:

17592733902055
17592733986620
17592733839582
17592734004880
17592733964943

这段代码运行用时共8925毫秒。

总结

引用RDD、DataFrame和DataSet的区别中的一句话:

RDD API是函数式的,强调不变性,在大部分场景下倾向于创建新对象而不是修改老对象。这一特点虽然带来了干净整洁的API,却也使得Spark应用程序在运行期倾向于创建大量临时对象,对GC造成压力。在现有RDD API的基础之上,我们固然可以利用mapPartitions方法来重载RDD单个分片内的数据创建方式,用复用可变对象的方式来减小对象分配和GC的开销,但这牺牲了代码的可读性,而且要求开发者对Spark运行时机制有一定的了解,门槛较高。另一方面,Spark SQL在框架内部已经在各种可能的情况下尽量重用对象,这样做虽然在内部会打破了不变性,但在将数据返回给用户时,还会重新转为不可变数据。利用 DataFrame API进行开发,可以免费地享受到这些优化效果。

同时从代码和测试结果中可以看出,用spark.sql比较方便快捷,而使用dataframe的api相较而言有些许速度提升,至于自己去进行rdd api编程如果不是为了解决功能上的问题,并没有例如速度或可维护性方面的优势。在日常(dataframe api 可满足的)应用中我更倾向于通过dataframe api接口进行spark应用开发。

  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丧心病狂の程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值