基于SPARK的淘宝用户购物行为可视化分析(调优版一)

基于SPARK的淘宝用户购物行为可视化分析(调优版一)

经过上一篇文章的训练,我们已经完成了一定的数据分析,接下来,这篇文章我们将进行SPARK调优。希望在这三台虚拟机跑出好成绩!

服务器配置

单机配置:

  • CPU:i5-12600KF
  • 内存:32GB

创建3台虚拟机,配置如下:

hadoop1102hadoop1103hadoop1104
CPU2处理器,共8内核2处理器,共8内核2处理器,共8内核
内存8GB4GB3GB
磁盘50GB50GB50GB
HDFSDataNode、NameNodeDataNodeDataNode、SecondaryNameNode
YARNNodeManagerNodeManager、ResourceManagerNodeManager

此外,还配置了zk、spark-hive等。软件版本设置如下…

  • zookpeeper:3.5.7
  • sqoop:1.4.6
  • jdk:1.8
  • scala:2.12
  • hadoop:3.1.3
  • spark:3.0.0
  • hive:3.1.2

配置具体可参考尚硅谷大数据课程。

前置配置

启动hadoop、yarn

start-dfs.sh
start-yarn.sh

启动历史日志服务器,不然啥也看不了

mr-jobhistory-daemon.sh start historyserver
$SPARK_HOME/sbin/start-history-server.sh

随便试试

创建spark应用

object UserScoreAPP {
    def main(args: Array[String]): Unit = {
        val startTime = System.currentTimeMillis()
        //1  初始化环境
        val sparkConf: SparkConf = new SparkConf().setAppName("UserScoreAPP") //.setMaster("local[*]")
        val sparkSession: SparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
        sparkSession.sql("use taobao")
        val sql =
            s"""
               |select  user_id,
               |        case when recent_rank < 671043 * 1 / 4 then 4
               |             when recent_rank < 671043 * 2 / 4 then 3
               |             when recent_rank < 671043 * 3 / 4 then 2
               |             else 1 end +
               |        case when frequency_rank < 671043 * 1 / 4 then 4
               |             when frequency_rank < 671043 * 2 / 4 then 3
               |             when frequency_rank < 671043 * 3 / 4 then 2
               |             else 1 end
               |            as score
               |from
               |    (
               |        select user_id, recent, dense_rank() over (order by recent asc) as recent_rank,  frequency, dense_rank() over (order by frequency desc) as frequency_rank
               |        from (
               |                 select user_id, datediff('2017-12-04', max(create_time)) as recent, count(behavior) frequency
               |                 from dwd_user_behavior
               |                 where behavior='buy'
               |                 group by user_id
               |             ) t1
               |    ) rfm
               |""".stripMargin
//        sparkSession.sql(sql).explain("extended")
        // 查看 RDD 的执行计划
//        val rdd = sparkSession.sql(sql).rdd
//        println(rdd.toDebugString)

        // 调用 RDD 的算子进行计算
//        rdd.foreach(println)

        sparkSession.sql(sql).show()
        val endTime = System.currentTimeMillis()
        val elapsedTime = (endTime - startTime) / 1000.0
        println(s"Execution time: ${elapsedTime}s")
    }
}

然后使用maven对这个应用打包,上传至/opt/module/taobao/。

cd /opt/module/taobao/
vim go_spark.sh

文本如下

#!/bin/bash

spark-submit \
--class com.xuhaoran.analyze.UserScoreAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar
chmod u+x go_spark.sh
./go_spark.sh

这时候,程序成功跑起来了。

点击url以开始分析

http://hadoop1103:8088/cluster/app/application_1681373698013_0022

成功跑通,显示如下:
在这里插入图片描述
在这里插入图片描述

运行时间130.774s,时间有点长啊!

为了方便调优学习,我把这一段sql代码转换成rdd代码,代码如下

object UserScoreRDDAPP {
    def main(args: Array[String]): Unit = {
        val startTime = System.currentTimeMillis()
        //1  初始化环境
        val sparkConf: SparkConf = new SparkConf().setAppName("UserScoreAPP") //.setMaster("local[*]")
        val sparkSession: SparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
//        sparkSession.sql("use taobao")
        import sparkSession.implicits._

        // Load the data and filter by 'buy' behavior
        val behaviorRDD = sparkSession.table("taobao.dwd_user_behavior").rdd
            .filter(row => row.getAs[String]("behavior") == "buy" && (row.getAs[String]("create_time") != null))


        // Calculate recent and frequency for each user_id
        val userRF = behaviorRDD
            .map(row => (row.getAs[Long]("user_id"), (Timestamp.valueOf(row.getAs[String]("create_time")), 1)))
            .reduceByKey((a, b) => (if (a._1.after(b._1)) a._1 else b._1, a._2 + b._2))
            .map { case (userId, (maxDate, frequency)) =>
                val recent = (Date.valueOf("2017-12-04").getTime - maxDate.getTime) / (24 * 3600 * 1000)
                (userId, recent, frequency)
            }

        // Calculate recent_rank and frequency_rank for each user_id
        val sortedRecent = userRF.sortBy(_._2).zipWithIndex.map { case (row, index) => (row._1, index + 1, row._3) }
        val sortedFrequency = userRF.sortBy(-_._3).zipWithIndex.map { case (row, index) => (row._1, index + 1) }

        val userRFM = sortedRecent
            .map { case (userId, recentRank, _) => (userId, recentRank) }
            .join(sortedFrequency)

        // Calculate the final score for each user_id
        val userScoreRDD = userRFM.map { case (userId, (recentRank, frequencyRank)) =>
            val recentScore = if (recentRank < 671043 * 1 / 4) 4 else if (recentRank < 671043 * 2 / 4) 3 else if (recentRank < 671043 * 3 / 4) 2 else 1
            val frequencyScore = if (frequencyRank < 671043 * 1 / 4) 4 else if (frequencyRank < 671043 * 2 / 4) 3 else if (frequencyRank < 671043 * 3 / 4) 2 else 1
            (userId, recentScore + frequencyScore)
        }
        // Perform operations on RDD
        userScoreRDD.foreach(println)
        val endTime = System.currentTimeMillis()
        val elapsedTime = (endTime - startTime) / 1000.0
        println(s"Execution time: ${elapsedTime}s")
    }
}	

并打包至hadoop1102中,修改go_spark.sh脚本内容

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

运行时间198.998s,这比sql还长得多啊!!!

正因为有点长,所以也是调优大展身手的时机!让我们马上开冲啦啦啦!!!

常规性能调优

参考文章

  1. Spark 优化 (一) --------- Spark 性能调优_spark调优_在森林中麋了鹿的博客-CSDN博客
  2. (2条消息) 第三部分:Spark调优篇_spark 调优_奔跑者-辉的博客-CSDN博客

常规性能调优一: 最优资源配置

首先是为任务分配更多的资源,在一定范围内尽可能用更多的资源,尽情压制机器所有的性能。可以分配的资源如下

名称说明
–num-executors配置Executor的数量
–driver-memory配置Driver内存(影响不大)
–executor-memory配置每个Executor的内存大小
–executor-cores配置每个Executor的CPU core数量

由于我的实验环境是yarn-cluster模式,那么这时候观察一下yarn resource资源,再来做决定。

在本机环境中,共有12GB Memory Total24 VCores Total,每个节点有4GB内存、8个VCores。Capacity Scheduler中Maximum Allocation:<memory:4096, vCores:4>

在这种条件下

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

队列仅用33.3%,分配了3个核以及4680的内存。

优化一:添加–num-executors

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--num-executors 3 \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

队列用了50%,分配了4个核以及6144的内存。

耗时 194.241s,好吧,没啥提升,让我们继续!

优化二:添加executor-cores

增加每个Executor的Cpu core个数,可以提高执行task的并行度,比如3个Executor,每个Executor有8个CPU core,那么可以并行执行24个task。

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-cores 3 \
--num-executors 3 \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

ok,这时候报错了

Requested resource=<memory:1408, vCores:8>, maximum allowed allocation=<memory:4096, vCores:4>, please note that maximum allowed allocation is calculated by scheduler based on maximum resource of registered NodeManagers, which might be less than configured maximum allocation=<memory:4096, vCores:4>

可以看到scheduler里面最多只可以分配<memory:4096, vCores:4>,而我上面的配置有问题,超过了yarn的配置大小,所以需要调整的。

vim yarn-site.xml进行修改

   <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>8000</value>
</property>

 <property>
        <name>yarn.scheduler.maximum-allocation-vcores</name>
        <value>12</value>
</property>

<!--  这里调成了8gb内存和12个vcores,即每个程序最高都能用这么多资源,相比尚硅谷原版的设置4gb和4Vcores,提高了两倍资源,原则上可以提升性能的了啊!  -->

然后保存yarn-site.xml并重启YARN服务,以使修改生效。

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-cores 3 \
--num-executors 3 \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

让我们继续执行,运行时间218.439s,一查yarn的管理网站,发现还是最高分配4个CPU核6144MB,很奇怪,也就是说根本就没提升。

优化三:添加–executor-memory

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-cores 2 \
--num-executors 3 \
--executor-memory 2g \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

分配4个CPU,分配9216内存,队列使用率75%

提高了这个按理说对性能有3点提升:

  1. 缓存更多的cache数据,写入磁盘的数据响应减少,减少磁盘IO
  2. 可以为shuffle操作提供更多内存,即有空间存放reduce端拉去的数据,写入磁盘的数据响应减少
  3. 为task提供更多的执行内存,避免频繁GC,提升整体性能。

但是!运行时间223.576s,真是难啊,为什么跟博客、课程学的不一样呢…

小结

既然最优资源配置不行,考虑一下是不是单机情况下虚拟机性能不ok产生的情况,但我目前也不清楚,所以只能对这个现象按下不表,先做点其他优化吧。

常规性能调优二: RDD优化

在这里插入图片描述

纵观整个spark运行,可以发现sortBy at UserScoreRDDAPP.scala:40运行时间最长

点进去看一下
在这里插入图片描述

可以发现map at UserScoreRDDAPP.scala 32的代码运行时间2.0min

在这里插入图片描述
在这里插入图片描述

调整初始分区数

基本可以确定前8个任务的运行时间都超长,就是在读取的时候运行时间很长,那么我们试试修改一下分区数?因为理想情况下,分区数应当接近 executor 核心总数(executor 数量 * 每个 executor 的核心数),以实现最佳并行度。

因此我们在代码中修改一下

val sparkConf: SparkConf = new SparkConf().setAppName("UserScoreAPP").set("spark.default.parallelism", 3*3+"")

这里就是将分区调成executor 数量 * 每个 executor 的核心数=9

效果还是有点显著的,运行时间Execution time: 172.823s

再次调executor-memory

把每个executor的内存调成3g,这时候再次运行

yarn中显示:队列占用100%,3个vcores,以及8704分配内存和3584保留内存。

运行时间161.153s

在这里插入图片描述

job 0的运行时间明显降低,从2.min降到1.8min,效果不错。

​ 疑问点:在这里调成了3g,发现hadoop1102不启动了,但是整体运行速度却加快。

好吧,那继续把 调整executor-cores=8,在程序内分区数设为 .set(“spark.default.parallelism”, 16+“”),希望可以让每台机器充分利用8个核

最后测得运行时间151.916s,job0的运行时间从1.8min降到1.5min,效果显著。

使用缓存

val userRF = behaviorRDD
    .map(row => (row.getAs[Long]("user_id"), (Timestamp.valueOf(row.getAs[String]("create_time")), 1)))
    .reduceByKey((a, b) => (if (a._1.after(b._1)) a._1 else b._1, a._2 + b._2))
    .map { case (userId, (maxDate, frequency)) =>
        val recent = (Date.valueOf("2017-12-04").getTime - maxDate.getTime) / (24 * 3600 * 1000)
        (userId, recent, frequency)
    }

// Calculate recent_rank and frequency_rank for each user_id
val sortedRecent = userRF.sortBy(_._2).zipWithIndex.map { case (row, index) => (row._1, index + 1, row._3) }
val sortedFrequency = userRF.sortBy(-_._3).zipWithIndex.map { case (row, index) => (row._1, index + 1) }

在这里加上 userRF.cache() 方法会对 userRF RDD 进行缓存。这意味着当 RDD 第一次被计算时,其结果将被存储在内存中,以便后续操作可以更快地访问它。这在对同一个 RDD 进行多次操作时非常有用,因为它可以避免重复计算 RDD 的数据。

在这个例子中,userRF 被用作 sortedRecentsortedFrequency 的输入。如果没有缓存,那么每次操作 userRF 时,都需要重新计算整个 userRF。通过使用 .cache() 方法,userRF 只需要计算一次,然后结果将被存储在内存中,这将加速后续的 sortedRecentsortedFrequency 计算。

因此运行速度提高,运行时间达到144.971s

常规性能调优三: JVM优化

上面的测试结果也可以发现一些问题,就是Job0的每个Task,GC时间占了一半,因此这里也需要进行调优。

在这里插入图片描述

我用的是java8,java8默认的gc收集器是parrel gc,在spark web ui可以看到其实程序跑的慢原因主要是gc占了多数,因此我想尝试调整至g1垃圾收集器

但是惊奇的发现:g1收集器的时候运行时间是496.727s

我上网搜了两点问题,这可能是g1在spark程序中不能跑得快的原因:

  1. G1 垃圾收集器的吞吐量并不是最高的,在一些需要高吞吐量的应用场景中,可能需要选择其他垃圾收集器。
  2. G1 垃圾收集器需要对堆内存进行分区,并对每个分区进行回收。在堆内存较小的情况下,分区的数量可能会增加,导致垃圾收集器的性能下降。

好吧,我再看看java8有什么更速的垃圾收集器吧,我需要的选型是高吞吐量的,低GC时间的。

​ 增加年轻代的比例可以将更多的内存分配给年轻代,从而提高程序的吞吐量和性能。年轻代是垃圾收集频率较高的区域,如果能够将更多的对象分配到年轻代中,就可以减少老年代的垃圾收集频率,从而减少应用程序的暂停时间,提高程序的性能和吞吐量。

这个时候我在想,慢的情况主要发生在读取数据后几个RDD,说明年轻代的问题比较大,那我是不是可以调高年轻代的比例呢?

#!/bin/bash
spark-submit \
--class com.xuhaoran.analyze.UserScoreRDDAPP \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-cores 8 \
--num-executors 3 \
--executor-memory 3g \
--conf "spark.executor.extraJavaOptions=-XX:NewRatio=3" \
 /opt/module/taobao/taobao-1.0-SNAPSHOT-jar-with-dependencies.jar

更慢了。。。用时174.273

失败!真的裂开!好吧,那继续下一个。

那动态分配新生代和老年代呢,这种情况如何?

--conf "spark.executor.extraJavaOptions=-XX:+UseAdaptiveSizePolicy" \

但是运行速度也是150多s,感觉跟前面做的最优速度没变化。

其他性能调优

比如说,按照(1条消息) Spark 优化 (一) --------- Spark 性能调优_spark调优_在森林中麋了鹿的博客-CSDN博客的介绍,其他调优方法还有:

  1. 常规性能调优
    1. 最优资源配置(已完成)
    2. RDD优化
      1. RDD 复用(已完成)
      2. RDD持久化
      3. RDD尽可能早的filter操作(已完成)
    3. 并行度调节(已完成)
    4. 广播大变量
    5. Kryo序列化
    6. 调节本地化等待时长
  2. 算子调优(已完成)
    1. forearchPartition优化数据库操作
    2. filter与coalesce的配合使用
    3. repartition解决SparkSQL低并行度问题
    4. reduceByKey预聚合(已完成)
  3. Shuffle调优
    1. 调节map端缓冲区大小
    2. 调节reduce端拉取数据缓冲区大小
    3. 调节reduce端拉去数据重试次数
    4. 调节reduce端拉取数据等待间隔
    5. 调节SortShuffle排序操作阈值
  4. JVM调优
    1. 降低cache操作的内存占比
    2. 调节Executor堆外内存
    3. 调节连接等待时长

总结

感谢各位大佬能看到这里了啦!

本次实验是我第一次调优,简直是糟糕透了,尝试了这么多配置感觉都是有问题的,目前还搞不通里面的门门道道,但至少运行时间从200s降至144s(比Spark-sql 高14s),也算是有点进步了。

此外,由于这次实验涉及的Shuffle不多,所以也并未做到Shuffle相关的调优,这些优化操作将在下一次实验进行,也期待下一次实验能够给大家展示成功的调优过程!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Spark淘宝大数据分析可视化是一种基于Spark框架的数据处理和可视化方法,用于从淘宝网站爬取商品价格和销量等数据。 首先,我们可以使用Spark的分布式计算能力,从淘宝网站爬取大量的商品数据。Spark提供了处理大规模数据的能力,可以将爬取的数据进行分布式处理,提高处理效率。 接下来,我们可以使用Spark的数据处理和分析功能对爬取到的数据进行清洗和筛选。通过数据清洗和筛选,我们可以去除无效或错误的数据,并提取出需要的商品价格和销量等信息。 然后,我们可以使用Spark的数据分析功能对清洗和筛选后的数据进行统计和计算。通过Spark的分布式计算能力,我们可以高效地进行复杂的数据统计和计算操作,例如计算商品的平均价格、销售额、销量等指标。 最后,我们可以使用Spark可视化功能对分析结果进行可视化展示。通过Spark可视化功能,我们可以将分析结果以图表或者图形的形式展示出来,提供直观的数据可视化效果。例如,我们可以使用图表来展示不同商品类别的销售情况,或者使用热力图来展示商品价格和销量的关系等。 基于Spark淘宝大数据分析可视化淘宝爬取商品价格销量,可以帮助商家或者分析师更好地了解商品的价格销量情况,优化商品的定价和销售策略,提高销售收益和市场竞争力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值