SparkSQL /DataFrame /Spark RDD谁快?

如题所示,SparkSQL /DataFrame /Spark RDD谁快?

按照官方宣传以及大部分人的理解,SparkSQL和DataFrame虽然基于RDD,但是由于对RDD做了优化,所以性能会优于RDD。

之前一直也是这么理解和操作的,直到最近遇到了一个场景,打破了这种不太准确的认识。

某些场景下,RDD要比DataFrame快,性能有天壤之别。

 

需求如下

以下两份数据求交集,结果输出url。

数据一,json格式,地址我们用path_json表示,大小10T,每一行数据格式:{"id":"md5字符串", "url":"https://www.thesaurus.com/","title":"sysnonyms and antonyms",xxx},大概20来个字段;

数据二,csv格式,地址我们用path_csv表示,大小50G,每一行数据格式:name url,2个字段,用\t隔开。

 

拿到需求后,迅速瞟了一眼数据,爽快答应需求方分分钟搞定。

此时此刻,必须得祭出宇宙Top N的IDE,结合我30多年的人生阅历和代码经验,瞬间雷光电闪,惊雷骤起,一顿操作猛如虎,天空飘过以下几行代码:

(老铁们,请自行安装python,pyspark,pycharm)

方案一


from pyspark.sql import SparkSession
def join_it():
    path_json = 'hdfs://i/love/you/'  # 数据大小10T, 5万分区
    path_csv  = 'hdfs://you/love/me'  # 数据大小50G
    path_save = 'hdfs://we/are/together'

    df1 = spark.read.json(path_json).select('url')
    df2 = spark.read.option('sep', '\t').schema('name string, url string').csv(path_csv)
    df1.join(df2) \
        .select(df1.url)\
        .coalesce(10000) \
        .write \
        .mode('overwrite') \
        .option('sep', '\t') \
        .csv(path_save)


if __name__ == '__main__':
    spark = SparkSession.builder.appName('pyspark').getOrCreate()
    sc = spark.sparkContext
    sc.setLogLevel("ERROR")
    #
    join_it()
    #
    spark.stop()

spark-submit提交任务到spark集群,参数根据自己的实际情况自行修改。

spark-submit \
    --master yarn \
    --deploy-mode client \
    --name 'i-live-you' \
    --queue 'you-love-me' \
    --driver-cores 10 \
    --driver-memory 30g \
    --num-executors 3000 \
    --executor-memory 30g \
    --executor-cores 4 \
    --archives 'hdfs://your-python-path-on-hdfs#pkg'
    --conf spark.yarn.appMasterEnv.PYSPARK_PYTHON='集群里面的python地址' \
    --conf spark.sql.shuffle.partitions=50000 \
    --conf spark.default.parallelism=50000 \
    --conf spark.task.maxFailures=20 \
    your-spark-script.py

如果需要在本机调试代码,spark的生成需要替换成如下,然后直接运行。调试通过后,仍然需要按照上述方式spark-submit提交任务到集群运行,由于数据量很大,需要在集群运行才能看出性能差异。

spark = SparkSession.builder.appName('pyspark').master('local[*]').getOrCreate()

又是一顿猛操作,提交任务后,嗡嗡叫的肚子提醒我,要去厕所烧一根香,拜拜佛,佛祖保佑无bug。

深圳的夏天,依旧不负众望的燥热。热情似火的太阳,伴着她最爱的紫外线和电磁波,循着外太空固定的轨道,迈着30万公里/秒的矫健步伐,到达这颗承载70亿人的蓝色星球,穿透层层蓝天白云,无私的照亮着广袤的深圳大地。

酷暑让人思想活跃,思绪万千。扯得有点远了,重来不重来了,接着写bug。

 

作为一个谨记厕所文化的人,蹲坑5分钟,方便你我他;蹲坑半小时,痔疮等着你。我选择了后者。

半小时过去了,时间随着大A股的大跌,瞬间来到了下午的收盘时间。果不其然,又一个下跌如期而至,就在这一刻,体内的混浊之气伴着伴随着收跌的股市排出体外。收拾干净后,我带着满身厕所的芬芳,回到了座位上。

再次打开电脑屏幕,spark任务还在慢悠悠的读取json文件,半小时才读取300G左右,10T的json文件按照这个速度,全部读完的好几天。此方案不可用。

 

方案二

果断改成RDD,然后用intersection求交集,果然快很多,10T跟50G求交集,12000cores,5分钟出结果。Spark任务提交同方案一,不再赘述。此方案可行

屁颠屁颠的把结果交付给需求方,大佬甚是满意地流出了开心的泪水

import json
from pyspark.sql import SparkSession

def join_it():
    path_json = 'hdfs://i/love/you/'  # 数据大小10T, 5万分区
    path_csv  = 'hdfs://you/love/me'  # 数据大小50G
    path_save = 'hdfs://we/are/together'
    #
    rdd1 = sc.textFile(path_json).map(lambda v: json.loads(v).get('url', '')).coalesce(50000)
    rdd2 = sc.textFile(path_csv).map(lambda v: v.split('\t')[1])
    rdd1.intersection(rdd2).coalesce(20000).saveAsTextFile(path_save)


if __name__ == '__main__':
    spark = SparkSession.builder.appName('pyspark').getOrCreate()
    sc = spark.sparkContext
    sc.setLogLevel("ERROR")
    #
    join_it()
    #
    spark.stop()

 

方案三

离下班实际还有30分钟,作为一个对技术有追求的资深码农,尝试用SparkSQL实现该功能。祭出代码,以资各位看官共享,新能跟RDD不相上下。此方案也可取。

import json
from pyspark.sql import SparkSession

def join_it():
    path_json = 'hdfs://i/love/you/'  # 数据大小10T, 5万分区
    path_csv  = 'hdfs://you/love/me'  # 数据大小50G
    path_save = 'hdfs://we/are/together'
    #
    sc.textFile(path_json).map(lambda v: (json.loads(v).get('url', ''),)).toDF(['url']).createOrReplaceTempView('a')
    spark.read.option('sep', '\t').schema('name string, url string').csv(path_csv).select('url').createOrReplaceTempView('b')

    sql = '''
    SELECT
        a.url
    FROM
        a
    JOIN
        b
    ON
        a.url=b.url
    '''
    spark.sql(sql).coalesce(20000).write.mode('overwrite').option('sep', '\t').csv(path_save)


if __name__ == '__main__':
    spark = SparkSession.builder.appName('pyspark').getOrCreate()
    sc = spark.sparkContext
    sc.setLogLevel("ERROR")
    #
    join_it()
    #
    spark.stop()

 

总结:

当遇到源数据是体量比较大的json或其他格式的时候,不要用spark.read的形式直接导入到DataFrame。

那要咋弄?可以先用RDD把源数据加载进来,然后再转化成DataFrame,后面用SparkSQL进行操作,如此可达到较好的性能效果。

 

posted @ 2020-08-15 21:57  凝望远处的天鹅  阅读(1261)  评论(2编辑  收藏  举报