Spark简单案例

138 阅读7分钟

案例来源:黑马程序员Spark全套视频教程教程 P138~P141

案例数据:mini.json(118.62M)

环境要求

 # 虚拟机需要安装配置 hadoop、hive、spark
 # 启动 hadoop
 $HADOOP_HOME/sbin/start-dfs.sh
 ​
 # 启动 hive 的 MetaStore 服务(元数据服务)
 nohup /export/server/hive/bin/hive --service metastore 2>&1 >> /var/log/metastore.log &
 ​
 # 启动 thriftserver
 /export/server/spark/sbin/start-thriftserver.sh \
     --hiveconf hive.server2.thrift.port=10000 \
     --hiveconf hive.server2.thrift.bind.host=node1
     --master local[2]
     
 # 查看 hadoop是否启动
 [root@node1 ~]# jps
 129904 NameNode
 1217 Jps
 130310 DataNode
 ​
 # 查看 MetaStore、thriftserver是否启动
 [root@node1 ~]# netstat -anp | grep 9083
 tcp6       0      0 :::9083                 :::*                    LISTEN      2823/java     
 [root@node1 ~]# netstat -anp | grep 10000
 tcp6       0      0 192.168.88.161:10000    :::*                    LISTEN      4673/java 

本地编码在虚拟机上运行的其他配置

 # Pycharm 通过 SSH 连接虚拟机的解释器
 /root/anaconda3/envs/pyspark_env/bin/python
 ​
 # Pycharm 远程连接到虚拟机,部署传输数据和代码,映射路径为
 /tmp/pycharm_project_105/

读取数据

 """
 需求1: 各省销售额的统计
 需求2: TOP3销售省份中, 有多少店铺达到过日销售额1000+
 需求3: TOP3省份中, 各省的平均单单价
 需求4: TOP3省份中, 各个省份的支付类型比例
 ​
 receivable:     订单金额
 storeProvince:  店铺省份
 dateTS:         订单的销售日期(时间戳)
 payType:        支付类型
 storeID:        店铺ID
 """
 # 创建 SparkSession
 spark = SparkSession.builder. \
     appName("案例"). \
     master("local[*]"). \
     config("spark.sql.shuffle.partitions", 2). \
     config("spark.sql.warehouse.dir", "hdfs://node1:8020/user/hive/warehouse"). \
     config("hive.metastore.uris", "thrift://node1:9083"). \
     enableHiveSupport(). \
     getOrCreate()
 ​
 # 读取数据
 # 省份信息, 缺失值过滤, 同时省份信息中 会有"null" 字符串
 # 订单的金额, 数据集中有的订单的金额是单笔超过10000的, 这些是测试数据
 # 列值裁剪(SparkSQL会自动做这个优化)
 file_path = "./data/input/mini.json"
 df = spark.read.format("json").load(file_path). \
     dropna(thresh=1, subset=["storeProvince"]). \
     filter("storeProvince != 'null'"). \
     filter("receivable < 10000"). \
     select("storeProvince", "storeID", "dateTS", "receivable", "payType")
     
 # +--------------+-------+-------------+----------+-------+
 # | storeProvince|storeID|       dateTS|receivable|payType|
 # +--------------+-------+-------------+----------+-------+
 # |        湖南省|   4064|1563758583000|      22.5| alipay|
 # |        湖南省|    718|1546737450000|       7.0| alipay|
 # |        湖南省|   1786|1546478081000|      10.0|   cash|
 # |        广东省|   3702|1559133703000|      10.5| wechat|
 # |广西壮族自治区|   1156|1548594458000|      10.0|   cash|
 # +--------------+-------+-------------+----------+-------+
 df.show(n=5)

需求1:各省销售额的统计

 # TODO 需求1: 各省销售额统计
 province_sale_df = df.groupBy("storeProvince").sum("receivable").\
     withColumnRenamed("sum(receivable)", "money").\
     withColumn("money", F.round("money", 2)).\
     orderBy("money", ascending=False)
 # +--------------+----------+
 # |storeProvince |money     |
 # +--------------+----------+
 # |广东省        |1713207.92|
 # |湖南省        |1701303.53|
 # |广西壮族自治区|37828.22  |
 # |北京市        |10926.91  |
 # |上海市        |7358.5    |
 # |江苏省        |6357.9    |
 # |浙江省        |4568.1    |
 # |山东省        |664.0     |
 # |江西省        |553.5     |
 # +--------------+----------+
 province_sale_df.show(truncate=False)
 ​
 # 得到的数据,写入到 mysql
 province_sale_df.write.mode("overwrite").\
     format("jdbc").\
     option("url","jdbc:mysql://node1:3306/bigdata?useSSL=false&useUnicode=true&characterEncoding=utf8").\
     option("dbtable","province_sale").\
     option("user","root").\
     option("password","123456").\
     option("encoding","utf-8").\
     save()
 ​
 # 写出Hive表 saveAsTable 可以写出表 要求已经配置好Spark On Hive, 配置好后
 # 会将表写入到Hive的数据仓库中
 # 在 saprk 数据库中查到数据
 province_sale_df.write.mode("overwrite").\
     saveAsTable("province_sale","parquet")

需求2:TOP3销售省份中, 有多少店铺达到过日销售额1000+

 # TODO 需求2: TOP3销售省份中, 有多少店铺达到过日销售额1000+
 # 2.1 先找到TOP3的销售省份
 top3_province_df = province_sale_df.select("storeProvince").\
     limit(3).\
     withColumnRenamed("storeProvince","top3_province")
 # +--------------+
 # | storeProvince|
 # +--------------+
 # |        广东省|
 # |        湖南省|
 # |广西壮族自治区|
 # +--------------+
 top3_province_df.show()
 ​
 # 2.2 和 原始的DF进行内关联, 数据关联后, 就是全部都是TOP3省份的销售数据了
 top3_province_df_joined = df.join(top3_province_df,on=(df["storeProvince"] == top3_province_df["top3_province"]))
 # +--------------+-------+-------------+----------+-------+--------------+
 # | storeProvince|storeID|       dateTS|receivable|payType| storeProvince|
 # +--------------+-------+-------------+----------+-------+--------------+
 # |        湖南省|   4064|1563758583000|      22.5| alipay|        湖南省|
 # |        湖南省|    718|1546737450000|       7.0| alipay|        湖南省|
 # |        湖南省|   1786|1546478081000|      10.0|   cash|        湖南省|
 # |        广东省|   3702|1559133703000|      10.5| wechat|        广东省|
 # |广西壮族自治区|   1156|1548594458000|      10.0|   cash|广西壮族自治区|
 # +--------------+-------+-------------+----------+-------+--------------+
 top3_province_df_joined.show(n=5)
 ​
 # 存档一下数据
 top3_province_df_joined.persist(StorageLevel.MEMORY_AND_DISK)
 ​
 # from_unixtime的精度是秒级, 数据的精度是毫秒级, 要对数据进行精度的裁剪
 # 根据 省份、店铺ID、时间(年月日)排序,计算日销售额
 province_hot_store_count_df = top3_province_df_joined.groupBy("storeProvince","storeID",
     F.from_unixtime(df["dateTS"].substr(0,10),format="yyyy-MM-dd").alias("day")).\
     sum("receivable").withColumnRenamed("sum(receivable)","money").\
     filter("money > 1000").\
     dropDuplicates(subset = ["storeID"]).\
     groupBy("storeProvince").\
     count()
 # +--------------+-----+
 # | storeProvince|count|
 # +--------------+-----+
 # |        广东省|  105|
 # |        湖南省|   97|
 # |广西壮族自治区|    3|
 # +--------------+-----+
 province_hot_store_count_df.show()
 ​
 # 得到的数据,写入到 mysql
 province_hot_store_count_df.write.mode("overwrite").\
     format("jdbc").\
     option("url","jdbc:mysql://node1:3306/bigdata?useSSL=false&useUnicode=true&characterEncoding=utf8").\
     option("dbtable","province_hot_store_count").\
     option("user","root").\
     option("password","123456").\
     option("encoding","utf-8").\
     save()
 ​
 # 写出Hive表 saveAsTable 可以写出表 要求已经配置好Spark On Hive, 配置好后
 # 会将表写入到Hive的数据仓库中
 # 在 saprk 数据库中查到数据
 province_hot_store_count_df.write.mode("overwrite").\
     saveAsTable("province_hot_store_count","parquet")

持久化存储 数据

 # 将数据 top3_province_df_joined 持久化到内存和磁盘中
 top3_province_df_joined.persist(StorageLevel.MEMORY_AND_DISK)
 ​
 ​
 # 持久化是指将数据存储在内存中,以便后续计算时能够快速访问,而不是每次都从原始数据源重新计算
 # persist() 可以避免重复计算,提高性能
 # StorageLevel.MEMORY_AND_DISK 存储级别,将数据存储在内存中,如果内存不足,超出的部分将被存储到磁盘上

列名冲突

 top3_province_df = province_sale_df.select("storeProvince").\
     limit(3)
 ​
 top3_province_df_joined = df.join(top3_province_df,on=(df["storeProvince"] == top3_province_df["storeProvince"]))
 ​
 province_hot_store_count_df = 
         top3_province_df_joined.groupBy("storeProvince","storeID",
                                         F.from_unixtime("dateTS",format="yyyy-MM-dd").alias("day")).\
         sum("receivable").\
         filter("sum(receivable) > 1000")
 ​
 # 报错
 # 在执行查询"top3_province_df_joined.groupBy("storeProvince"..."时
 # storeProvince 这个字段存在于多个数据框中,导致 Spark 无法确定你指的是哪个字段。
 pyspark.sql.utils.AnalysisException: Reference 'storeProvince' is ambiguous, could be: storeProvince, storeProvince.
 ​
 # 解决方法
 # 修改其中一个列名
 top3_province_df = province_sale_df.select("storeProvince").\
     limit(3).\
     withColumnRenamed("storeProvince","top3_province")

时间戳转为 yyyy-MM-dd 格式

 # 修改前
 province_hot_store_count_df = 
         top3_province_df_joined.groupBy("storeProvince","storeID",
                 F.from_unixtime(df["dateTS"],format="yyyy-MM-dd").alias("day")).\
         sum("receivable").\
         filter("sum(receivable) > 1000")
 ​
 # +-------------+-------+------------+---------------+
 # |storeProvince|storeID|         day|sum(receivable)|
 # +-------------+-------+------------+---------------+
 # |       湖南省|   2555|+50971-03-19|         3052.0|
 # |       湖南省|   3116|+51094-08-30|         1376.0|
 # |       广东省|   4111|+51361-12-26|         1150.0|
 # |       广东省|   1475|+51132-03-18|         1279.0|
 # |       广东省|   1865|+51051-04-06|        4113.99|
 # +-------------+-------+------------+---------------+
 province_hot_store_count_df.show(n=5)
 ​
 # 修改后
 province_hot_store_count_df = 
         top3_province_df_joined.groupBy("storeProvince","storeID",
                 F.from_unixtime(df["dateTS"].substr(0,10),format="yyyy-MM-dd").alias("day")).\
         sum("receivable").\
         filter("sum(receivable) > 1000")
 # +-------------+-------+----------+---------------+
 # | storeProvince | storeID | day | sum(receivable) |
 # +-------------+-------+----------+---------------+
 # | 湖南省 | 643 | 2018 - 12 - 29 | 1040.0 |
 # | 广东省 | 2647 | 2019 - 03 - 14 | 3619.0 |
 # | 湖南省 | 2555 | 2019 - 01 - 01 | 3052.0 |
 # | 广东省 | 3695 | 2019 - 07 - 09 | 1093.0 |
 # | 湖南省 | 627 | 2019 - 02 - 03 | 3040.0 |
 # +-------------+-------+----------+---------------+
 province_hot_store_count_df.show()

需求3:TOP3省份中, 各省的平均单价

 # TODO 需求3: TOP3 省份中 各个省份的平均订单价格(单价)
 top3_province_order_avg_df = top3_province_df_joined.groupBy("storeProvince").\
     avg("receivable").withColumnRenamed("avg(receivable)","money").\
     withColumn("money",F.round("money",2)).\
     orderBy("money")
 # +--------------+-----+
 # | storeProvince|money|
 # +--------------+-----+
 # |        广东省|32.81|
 # |        湖南省|36.86|
 # |广西壮族自治区|40.03|
 # +--------------+-----+
 top3_province_order_avg_df.show()
 ​
 # 得到的数据,写入到 mysql
 top3_province_order_avg_df.write.mode("overwrite").\
     format("jdbc").\
     option("url","jdbc:mysql://node1:3306/bigdata?useSSL=false&useUnicode=true&characterEncoding=utf8").\
     option("dbtable","top3_province_order_avg_df").\
     option("user","root").\
     option("password","123456").\
     option("encoding","utf-8").\
     save()
 ​
 # 写出Hive表 saveAsTable 可以写出表 要求已经配置好Spark On Hive, 配置好后
 # 会将表写入到Hive的数据仓库中
 # 在 saprk 数据库中查到数据
 top3_province_order_avg_df.write.mode("overwrite").\
     saveAsTable("top3_province_order_avg_df","parquet")

需求4:TOP3省份中, 各个省份的支付类型比例

 # TODO 需求4: TOP3 省份中, 各个省份支付类型的支付比例
 # 创建临时表
 top3_province_df_joined.createTempView("province_pay")
 ​
 # 将 percent 修改为 % 2显示
 def udf_func(percent):
     return str(round(percent * 100,2)) + "%"
 # 注册UDF
 my_udf = F.udf(udf_func,StringType())
 ​
 pay_type_df = spark.sql("""
     select storeProvince,payType,(COUNT(payType)/total) as percent from 
     (select storeProvince,payType,count(1) over(PARTITION BY storeProvince) as total from province_pay)
     group by storeProvince,payType,total
     """).withColumn("percent",my_udf("percent"))
 ​
 # +--------------+--------+-------+
 # | storeProvince| payType|percent|
 # +--------------+--------+-------+
 # |        广东省|  wechat| 39.48%|
 # |        广东省|    cash| 52.91%|
 # |        广东省|bankcard|  0.72%|
 # |        广东省|  alipay|  6.88%|
 # |广西壮族自治区|    cash| 73.23%|
 # |广西壮族自治区|  wechat| 21.59%|
 # |广西壮族自治区|  alipay|  4.23%|
 # |广西壮族自治区|bankcard|  0.95%|
 # |        湖南省|  alipay|  4.24%|
 # |        湖南省|    cash| 70.81%|
 # |        湖南省|  wechat| 24.88%|
 # |        湖南省|bankcard|  0.06%|
 # +--------------+--------+-------+
 pay_type_df.show()
 ​
 # 得到的数据,写入到 mysql
 pay_type_df.write.mode("overwrite").\
     format("jdbc").\
     option("url","jdbc:mysql://node1:3306/bigdata?useSSL=false&useUnicode=true&characterEncoding=utf8").\
     option("dbtable","pay_type").\
     option("user","root").\
     option("password","123456").\
     option("encoding","utf-8").\
     save()
 ​
 # 写出Hive表 saveAsTable 可以写出表 要求已经配置好Spark On Hive, 配置好后
 # 会将表写入到Hive的数据仓库中
 # 在 saprk 数据库中查到数据
 pay_type_df.write.mode("overwrite").\
     saveAsTable("default.pay_type","parquet")
###