pyspark系列3-spark核心之RDD介紹

一.RDD概念

RDD(resilient distributed dataset ,彈性分布式數據集),是 Spark 中最基礎的抽象。它表示了一個可以并行操作的、不可變得、被分區了的元素集合。用戶不需要關心底層復雜的抽象處理,直接使用方便的算子處理和計算就可以了。

1.1 RDD的特點

1) . 分布式
RDD是一個抽象的概念,RDD在spark driver中,通過RDD來引用數據,數據真正存儲在節點機的partition上。

2). 只讀
在Spark中RDD一旦生成了,就不能修改。
那么為什么要設置為只讀,設置為只讀的話,因為不存在修改,并發的吞吐量就上來了。

3). 血緣關系
我們需要對RDD進行一系列的操作,因為RDD是只讀的,我們只能不斷的生產新的RDD,這樣,新的RDD與原來的RDD就會存在一些血緣關系。

Spark會記錄這些血緣關系,在后期的容錯上會有很大的益處。

4). 緩存
當一個 RDD 需要被重復使用時,或者當任務失敗重新計算的時候,這時如果將 RDD 緩存起來,就可以避免重新計算,保證程序運行的性能。

RDD 的緩存有三種方式:cache、persist、checkPoint。

  1. cache
    cache 方法不是在被調用的時候立即進行緩存,而是當觸發了 action 類型的算子之后,才會進行緩存。

  2. cache 和 persist 的區別
    其實 cache 底層實際調用的就是 persist 方法,只是緩存的級別默認是 MEMORY_ONLY,而 persist 方法可以指定其他的緩存級別。

  3. cache 和 checkPoint 的區別
    checkPoint 是將數據緩存到本地或者 HDFS 文件存儲系統中,當某個節點的 executor 宕機了之后,緩存的數據不會丟失,而通過 cache 緩存的數據就會丟掉。

checkPoint 的時候會把 job 從開始重新再計算一遍,因此在 checkPoint 之前最好先進行一步 cache 操作,cache 不需要重新計算,這樣可以節省計算的時間。

  1. persist 和 checkPoint 的區別
    persist 也可以選擇將數據緩存到磁盤當中,但是它交給 blockManager 管理的,一旦程序運行結束,blockManager 也會被停止,這時候緩存的數據就會被釋放掉。而 checkPoint 持久化的數據并不會被釋放,是一直存在的,可以被其它的程序所使用。

1.2 RDD的核心屬性

RDD 調度和計算都依賴于這五屬性
1). 分區列表
Spark RDD 是被分區的,每一個分區都會被一個計算任務 (Task) 處理,分區數決定了并行計算的數量,RDD 的并行度默認從父 RDD 傳給子 RDD。默認情況下,一個 HDFS 上的數據分片就是一個 partiton,RDD 分片數決定了并行計算的力度,可以在創建 RDD 時指定 RDD 分片個數,如果不指定分區數量,當 RDD 從集合創建時,則默認分區數量為該程序所分配到的資源的 CPU 核數 (每個 Core 可以承載 2~4 個 partition),如果是從 HDFS 文件創建,默認為文件的 Block 數。

2). 依賴列表
由于 RDD 每次轉換都會生成新的 RDD,所以 RDD 會形成類似流水線一樣的前后依賴關系,當然寬依賴就不類似于流水線了,寬依賴后面的 RDD 具體的數據分片會依賴前面所有的 RDD 的所有數據分片,這個時候數據分片就不進行內存中的 Pipeline,一般都是跨機器的,因為有前后的依賴關系,所以當有分區的數據丟失時, Spark 會通過依賴關系進行重新計算,從而計算出丟失的數據,而不是對 RDD 所有的分區進行重新計算。RDD 之間的依賴有兩種:窄依賴 ( Narrow Dependency) 和寬依賴 ( Wide Dependency)。RDD 是 Spark 的核心數據結構,通過 RDD 的依賴關系形成調度關系。通過對 RDD 的操作形成整個 Spark 程序。

3). Compute函數,用于計算RDD各分區的值
每個分區都會有計算函數, Spark 的 RDD 的計算函數是以分片為基本單位的,每個 RDD 都會實現 compute 函數,對具體的分片進行計算,RDD 中的分片是并行的,所以是分布式并行計算,有一點非常重要,就是由于 RDD 有前后依賴關系,遇到寬依賴關系,如 reduce By Key 等這些操作時劃分成 Stage, Stage 內部的操作都是通過 Pipeline 進行的,在具體處理數據時它會通過 Blockmanager 來獲取相關的數據,因為具體的 split 要從外界讀數據,也要把具體的計算結果寫入外界,所以用了一個管理器,具體的 split 都會映射成 BlockManager 的 Block,而體的 splt 會被函數處理,函數處理的具體形式是以任務的形式進行的。

4). 分區策略(可選)
每個 key-value 形式的 RDD 都有 Partitioner 屬性,它決定了 RDD 如何分區。當然,Partiton 的個數還決定了每個 Stage 的 Task 個數。RDD 的分片函數可以分區 ( Partitioner),可傳入相關的參數,如 Hash Partitioner 和 Range Partitioner,它本身針對 key- value 的形式,如果不是 key-ale 的形式它就不會有具體的 Partitioner, Partitioner 本身決定了下一步會產生多少并行的分片,同時它本身也決定了當前并行 ( Parallelize) Shuffle 輸出的并行數據,從而使 Spak 具有能夠控制數據在不同結點上分區的特性,用戶可以自定義分區策略,如 Hash 分區等。 spark 提供了 partition By 運算符,能通過集群對 RDD 進行數據再分配來創建一個新的 RDD。

5). 優先位置列表(可選,HDFS實現數據本地化,避免數據移動)
優先位置列表會存儲每個 Partition 的優先位置,對于一個 HDFS 文件來說,就是每個 Partition 塊的位置。觀察運行 Spark 集群的控制臺就會發現, Spark 在具體計算、具體分片以前,它已經清楚地知道任務發生在哪個結點上,也就是說任務本身是計算層面的、代碼層面的,代碼發生運算之前它就已經知道它要運算的數據在什么地方,有具體結點的信息。這就符合大數據中數據不動代碼動的原則。數據不動代碼動的最高境界是數據就在當前結點的內存中。這時候有可能是 Memory 級別或 Tachyon 級別的, Spark 本身在進行任務調度時會盡可能地將任務分配到處理數據的數據塊所在的具體位置。據 Spark 的 RDD。 Scala 源代碼函數 getParferredlocations 可知,每次計算都符合完美的數據本地性。

二.操作RDD

2.1 PySpark介紹

PySpark實現了Spark對于Python的API,通過它,用戶可以編寫運行在Spark之上的Python程序,從而利用到Spark分布式計算的特點。

Python API的實現依賴于Java的API,Python程序端的SparkContext通過py4j調用JavaSparkContext,后者是對Scala的SparkContext的一個封裝。而對RDD進行轉換和操作的函數由用戶通過Python程序來定義,這些函數會被序列化然后發送到各個worker,然后每一個worker啟動一個Python進程來執行反序列化之后的函數,通過管道拿到執行之后的結果。

1). Python程序的啟動
和Scala程序一樣,Python程序也是通過SparkSubmit提交得以執行,在SparkSubmit中會判斷提交的程序是否為Python,如果是,則設置mainClass為PythonRunner。在PythonRunner中,會根據配置選項,以及用戶通過命令行提供的--py-files選項,設置好PYTHONPATH,然后啟動一個Java的GatewayServer用來被Python程序調用,然后以用戶配置的PYSPARK_PYTHON選項作為Python解釋器,執行Python文件,至此用戶的Python程序得以啟動。

2). SparkContext
和在Scala中一樣,SparkContext是調用Spark進行計算的入口。在Python的context.py中定義了類SparkContext,它封裝了一個JavaSparkContext作為它的_jsc屬性。在初始化SparkContext時,首先會調用java_gateway.py中定義的launch_gateway方法來初始化JavaGateWay,在launch_gateway中會引入在Spark中定義的類到SparkContext的屬性_jvm,比如:java_import(gateway.jvm, "org.apache.spark.SparkConf")。這樣在Python中就可以通過SparkContext._jvm.SparkConf引用在Scala中定義的SparkConf這個類,可以實例化這個類的對象,可以調用對象的方法等。在初始化完畢之后,用戶就可以調用SparkContext中的方法了,比如textFile和parallelize。

3). RDD
Python中的RDD對Spark中的RDD進行了一次封裝,每一個RDD都對應了一個反序列化的函數。這是因為,盡管在Spark中RDD的元素可以具有任意類型,提供給JavaSparkContext中生成的RDD的只具有Array[Byte]類型,也就是說JavaSparkContext的函數返回值是JavaRDD[Array[Byte]],這樣,Python程序需要把對象先序列化成byte數組,然后把它分布到各個節點進行計算。計算完之后再反序列化成Python的對象。(這其中有一個特殊情況,就是JavaSparkContext返回的是JavaRDD[String],可以把它當成是不需要序列化和反序列化的對象。)在Spark中不需要知道Array[Byte]反序列化之后是什么。如何序列化和反序列化、如何對這些Array[Byte]進行轉換和操作都由Python程序來控制,Spark只是負責資源的調度,負責如何把這些計算分配到各個節點上去執行。

2.2 PySpark環境配置

安裝好spark后,直接輸入pyspark,可調出pyspark工作界面

[root@hp2 ~]# pyspark
Python 2.7.5 (default, Apr  2 2020, 13:16:51) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
21/04/13 08:37:30 WARN lineage.LineageWriter: Lineage directory /var/log/spark/lineage doesn't exist or is not writable. Lineage for this application will be disabled.
21/04/13 08:37:30 WARN lineage.LineageWriter: Lineage directory /var/log/spark/lineage doesn't exist or is not writable. Lineage for this application will be disabled.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.4.0-cdh6.3.1
      /_/

Using Python version 2.7.5 (default, Apr  2 2020 13:16:51)
SparkSession available as 'spark'.
>>> 

1). 引入Python中pyspark工作模塊

import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#任何Spark程序都是SparkContext開始的,SparkContext的初始化需要一個SparkConf對象,SparkConf包含了Spark集群配置的各種參數(比如主節點的URL)。初始化后,就可以使用SparkContext對象所包含的各種方法來創建和操作RDD和共享變量。Spark shell會自動初始化一個SparkContext(在Scala和Python下可以,但不支持Java)。
#getOrCreate表明可以視情況新建session或利用已有的session

2). Python腳本執行
python腳本中需要在開頭導入spark相關模塊,調用時使用spark-submit提交,如下所示:

spark-submit --master local xxxx.py
spark-submit --master yarn --deploy-mode cluster xxxx.py
spark-submit --master yarn --deploy-mode client xxxx.py

2.3 PySpark使用

2.3.1 初始化Spark

編寫Spark程序的第一件事情就是創建SparkContext對象,SparkContext負責連接到集群。創建SparkContext先要創建SparkConf對象,該對象可以定義我們Spark程序的相關參數。

conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)

其中appName是程序名稱,它會顯示在集群狀態界面上;master是要提交到的集群的地址

2.3.2 初始化RDD

RDD(Resilient Distributed Datasets)是Spark中抽象出來的彈性分布式數據集,其本質上是一個只讀的分區記錄集合。每個RDD可以分成多個分區,每個分區就是一個數據集片段。

創建RDD有兩種方式:一種是將驅動程序中的已有集合平行化;另外一種是引用外部存儲系統的數據集,例如共享文件系統,HDFS, HBase, 或者其他類似Hadoop的數據源.

1). 通過集合創建RDD
代碼:

import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)

/* 通過集合創建RDD */
rdd=sc.parallelize([1,2,3,4,5]) 
rdd
-- 查看list被分成了幾部分
rdd.getNumPartitions()
-- 查看分區狀況
rdd.glom().collect()
rdd.collect()

測試記錄:

[root@hp2 pyspark]# pyspark
Python 2.7.5 (default, Apr  2 2020, 13:16:51) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
21/04/13 10:06:01 WARN cluster.YarnSchedulerBackend$YarnSchedulerEndpoint: Attempted to request executors before the AM has registered!
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.4.0-cdh6.3.1
      /_/

Using Python version 2.7.5 (default, Apr  2 2020 13:16:51)
SparkSession available as 'spark'.
>>> 
>>> import pyspark
>>> from pyspark import SparkContext as sc
>>> from pyspark import SparkConf
>>> conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
>>> sc=SparkContext.getOrCreate(conf)
>>> 
>>> 
/* 通過集合創建RDD */
>>> rdd=sc.parallelize([1,2,3,4,5]) 
>>> rdd
ParallelCollectionRDD[0] at parallelize at PythonRDD.scala:195
>>> 
-- 查看list被分成了幾部分
>>> rdd.getNumPartitions()
2
>>> 
-- 查看分區狀況
>>> rdd.glom().collect()
[[1, 2], [3, 4, 5]]                                                             
>>> 
>>> rdd.collect()
[1, 2, 3, 4, 5]
>>> 

2). 通過文件創建rdd
讀取一個idcard.txt,獲取年齡和性別。

代碼:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from datetime import date
from pyspark import SparkContext, SparkConf
import os

#設置環境變量
os.environ['JAVA_HOME'] = '/usr/java/jdk1.8.0_181'                      # java環境配置
os.environ['HADOOP_HOME'] = '/opt/cloudera/parcels/CDH-6.3.1-1.cdh6.3.1.p0.1470567/lib/hadoop'          # hadoop安裝目錄
os.environ['SPARK_HOME'] = '/opt/cloudera/parcels/CDH-6.3.1-1.cdh6.3.1.p0.1470567/lib/spark'  # 設置spark安裝目錄

today = date.today()

# 設置Spark程序運行的地方,此處設置運行在本地模式,啟動2個線程分析數據
spark_conf = SparkConf().setMaster("local[*]").setAppName("Get Idcard")
sc = SparkContext(conf = spark_conf)

filename = 'file:///home/pyspark/idcard.txt'
rdd = sc.textFile(filename)


idcards = rdd.collect()

for idcard in idcards:
   # 求出年齡
    my_idcard = idcard
    birh_year = my_idcard[6:10]

    age = int(today.year) - int(birh_year)
    print "年齡是:" +  str(age)

    # 求性別
    sex_type = my_idcard[-1]
    sex_result = int(sex_type)%2
    if sex_result == 1:
       sex = '男'
    else:
       sex = '女'

    print "性別是:" + sex

    print("\n")

sc.stop()

測試記錄:

[root@hp2 pyspark]# spark-submit test2.py 
21/04/13 10:45:00 INFO spark.SparkContext: Running Spark version 2.4.0-cdh6.3.1
21/04/13 10:45:00 INFO logging.DriverLogger: Added a local log appender at: /tmp/spark-5904579a-d4c0-4a0a-8ffe-e7af09931299/__driver_logs__/driver.log
21/04/13 10:45:00 INFO spark.SparkContext: Submitted application: Get Idcard
21/04/13 10:45:00 INFO spark.SecurityManager: Changing view acls to: root
21/04/13 10:45:00 INFO spark.SecurityManager: Changing modify acls to: root
21/04/13 10:45:00 INFO spark.SecurityManager: Changing view acls groups to: 
21/04/13 10:45:00 INFO spark.SecurityManager: Changing modify acls groups to: 
21/04/13 10:45:00 INFO spark.SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users  with view permissions: Set(root); groups with view permissions: Set(); users  with modify permissions: Set(root); groups with modify permissions: Set()
21/04/13 10:45:00 INFO util.Utils: Successfully started service 'sparkDriver' on port 37239.
21/04/13 10:45:00 INFO spark.SparkEnv: Registering MapOutputTracker
21/04/13 10:45:00 INFO spark.SparkEnv: Registering BlockManagerMaster
21/04/13 10:45:00 INFO storage.BlockManagerMasterEndpoint: Using org.apache.spark.storage.DefaultTopologyMapper for getting topology information
21/04/13 10:45:00 INFO storage.BlockManagerMasterEndpoint: BlockManagerMasterEndpoint up
21/04/13 10:45:00 INFO storage.DiskBlockManager: Created local directory at /tmp/blockmgr-6a0ee83c-f223-422a-99f2-66a027cefd55
21/04/13 10:45:00 INFO memory.MemoryStore: MemoryStore started with capacity 366.3 MB
21/04/13 10:45:00 INFO spark.SparkEnv: Registering OutputCommitCoordinator
21/04/13 10:45:01 INFO util.log: Logging initialized @1715ms
21/04/13 10:45:01 INFO server.Server: jetty-9.3.z-SNAPSHOT, build timestamp: 2018-09-05T05:11:46+08:00, git hash: 3ce520221d0240229c862b122d2b06c12a625732
21/04/13 10:45:01 INFO server.Server: Started @1786ms
21/04/13 10:45:01 INFO server.AbstractConnector: Started ServerConnector@2a2e00fb{HTTP/1.1,[http/1.1]}{0.0.0.0:4040}
21/04/13 10:45:01 INFO util.Utils: Successfully started service 'SparkUI' on port 4040.
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@52fd23a9{/jobs,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@524f7a39{/jobs/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@1e124efa{/jobs/job,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@7eee02a9{/jobs/job/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@57d931ff{/stages,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@5b30c060{/stages/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@1dad9173{/stages/stage,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@507e4c6a{/stages/stage/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@21a9791f{/stages/pool,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@733a2714{/stages/pool/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@7b51b4f5{/storage,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@596eaae9{/storage/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@3365515a{/storage/rdd,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@1145c2a5{/storage/rdd/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@67c53a64{/environment,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@4b50fd97{/environment/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@52269a26{/executors,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@6dc93a2d{/executors/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@23113bea{/executors/threadDump,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@6f61d65a{/executors/threadDump/json,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@970863a{/static,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@4a6109c2{/,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@62b52167{/api,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@583d3e41{/jobs/job/kill,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@48f167f0{/stages/stage/kill,null,AVAILABLE,@Spark}
21/04/13 10:45:01 INFO ui.SparkUI: Bound SparkUI to 0.0.0.0, and started at http://hp2:4040
21/04/13 10:45:01 INFO executor.Executor: Starting executor ID driver on host localhost
21/04/13 10:45:01 INFO util.Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 44348.
21/04/13 10:45:01 INFO netty.NettyBlockTransferService: Server created on hp2:44348
21/04/13 10:45:01 INFO storage.BlockManager: Using org.apache.spark.storage.RandomBlockReplicationPolicy for block replication policy
21/04/13 10:45:01 INFO storage.BlockManagerMaster: Registering BlockManager BlockManagerId(driver, hp2, 44348, None)
21/04/13 10:45:01 INFO storage.BlockManagerMasterEndpoint: Registering block manager hp2:44348 with 366.3 MB RAM, BlockManagerId(driver, hp2, 44348, None)
21/04/13 10:45:01 INFO storage.BlockManagerMaster: Registered BlockManager BlockManagerId(driver, hp2, 44348, None)
21/04/13 10:45:01 INFO storage.BlockManager: external shuffle service port = 7337
21/04/13 10:45:01 INFO storage.BlockManager: Initialized BlockManager: BlockManagerId(driver, hp2, 44348, None)
21/04/13 10:45:01 INFO handler.ContextHandler: Started o.s.j.s.ServletContextHandler@29f4468f{/metrics/json,null,AVAILABLE,@Spark}
21/04/13 10:45:02 INFO scheduler.EventLoggingListener: Logging events to hdfs://nameservice1/user/spark/applicationHistory/local-1618281901231
21/04/13 10:45:02 INFO spark.SparkContext: Registered listener com.cloudera.spark.lineage.NavigatorAppListener
21/04/13 10:45:02 INFO logging.DriverLogger$DfsAsyncWriter: Started driver log file sync to: /user/spark/driverLogs/local-1618281901231_driver.log
21/04/13 10:45:02 INFO memory.MemoryStore: Block broadcast_0 stored as values in memory (estimated size 290.4 KB, free 366.0 MB)
21/04/13 10:45:02 INFO memory.MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 28.4 KB, free 366.0 MB)
21/04/13 10:45:02 INFO storage.BlockManagerInfo: Added broadcast_0_piece0 in memory on hp2:44348 (size: 28.4 KB, free: 366.3 MB)
21/04/13 10:45:02 INFO spark.SparkContext: Created broadcast 0 from textFile at NativeMethodAccessorImpl.java:0
21/04/13 10:45:02 INFO mapred.FileInputFormat: Total input files to process : 1
21/04/13 10:45:02 INFO spark.SparkContext: Starting job: collect at /home/pyspark/test2.py:23
21/04/13 10:45:02 INFO scheduler.DAGScheduler: Got job 0 (collect at /home/pyspark/test2.py:23) with 2 output partitions
21/04/13 10:45:02 INFO scheduler.DAGScheduler: Final stage: ResultStage 0 (collect at /home/pyspark/test2.py:23)
21/04/13 10:45:02 INFO scheduler.DAGScheduler: Parents of final stage: List()
21/04/13 10:45:02 INFO scheduler.DAGScheduler: Missing parents: List()
21/04/13 10:45:02 INFO scheduler.DAGScheduler: Submitting ResultStage 0 (file:///home/pyspark/idcard.txt MapPartitionsRDD[1] at textFile at NativeMethodAccessorImpl.java:0), which has no missing parents
21/04/13 10:45:02 INFO memory.MemoryStore: Block broadcast_1 stored as values in memory (estimated size 3.4 KB, free 366.0 MB)
21/04/13 10:45:03 INFO memory.MemoryStore: Block broadcast_1_piece0 stored as bytes in memory (estimated size 2.0 KB, free 366.0 MB)
21/04/13 10:45:03 INFO storage.BlockManagerInfo: Added broadcast_1_piece0 in memory on hp2:44348 (size: 2.0 KB, free: 366.3 MB)
21/04/13 10:45:03 INFO spark.SparkContext: Created broadcast 1 from broadcast at DAGScheduler.scala:1164
21/04/13 10:45:03 INFO scheduler.DAGScheduler: Submitting 2 missing tasks from ResultStage 0 (file:///home/pyspark/idcard.txt MapPartitionsRDD[1] at textFile at NativeMethodAccessorImpl.java:0) (first 15 tasks are for partitions Vector(0, 1))
21/04/13 10:45:03 INFO scheduler.TaskSchedulerImpl: Adding task set 0.0 with 2 tasks
21/04/13 10:45:03 INFO scheduler.TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, localhost, executor driver, partition 0, PROCESS_LOCAL, 7889 bytes)
21/04/13 10:45:03 INFO scheduler.TaskSetManager: Starting task 1.0 in stage 0.0 (TID 1, localhost, executor driver, partition 1, PROCESS_LOCAL, 7889 bytes)
21/04/13 10:45:03 INFO executor.Executor: Running task 0.0 in stage 0.0 (TID 0)
21/04/13 10:45:03 INFO executor.Executor: Running task 1.0 in stage 0.0 (TID 1)
21/04/13 10:45:03 INFO rdd.HadoopRDD: Input split: file:/home/pyspark/idcard.txt:0+104
21/04/13 10:45:03 INFO rdd.HadoopRDD: Input split: file:/home/pyspark/idcard.txt:104+105
21/04/13 10:45:03 INFO executor.Executor: Finished task 0.0 in stage 0.0 (TID 0). 867 bytes result sent to driver
21/04/13 10:45:03 INFO executor.Executor: Finished task 1.0 in stage 0.0 (TID 1). 848 bytes result sent to driver
21/04/13 10:45:03 INFO scheduler.TaskSetManager: Finished task 1.0 in stage 0.0 (TID 1) in 90 ms on localhost (executor driver) (1/2)
21/04/13 10:45:03 INFO scheduler.TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 107 ms on localhost (executor driver) (2/2)
21/04/13 10:45:03 INFO scheduler.TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool 
21/04/13 10:45:03 INFO scheduler.DAGScheduler: ResultStage 0 (collect at /home/pyspark/test2.py:23) finished in 0.168 s
21/04/13 10:45:03 INFO scheduler.DAGScheduler: Job 0 finished: collect at /home/pyspark/test2.py:23, took 0.232997 s
年齡是:52
性別是:女


年齡是:43
性別是:男


年齡是:52
性別是:男


年齡是:45
性別是:女


年齡是:58
性別是:女


年齡是:42
性別是:女


年齡是:65
性別是:女


年齡是:52
性別是:女


年齡是:41
性別是:女


年齡是:52
性別是:男


年齡是:31
性別是:男


21/04/13 10:45:03 INFO server.AbstractConnector: Stopped Spark@2a2e00fb{HTTP/1.1,[http/1.1]}{0.0.0.0:4040}
21/04/13 10:45:03 INFO ui.SparkUI: Stopped Spark web UI at http://hp2:4040
21/04/13 10:45:03 INFO spark.MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
21/04/13 10:45:03 INFO memory.MemoryStore: MemoryStore cleared
21/04/13 10:45:03 INFO storage.BlockManager: BlockManager stopped
21/04/13 10:45:03 INFO storage.BlockManagerMaster: BlockManagerMaster stopped
21/04/13 10:45:03 INFO scheduler.OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
21/04/13 10:45:03 INFO spark.SparkContext: Successfully stopped SparkContext
21/04/13 10:45:03 INFO util.ShutdownHookManager: Shutdown hook called
21/04/13 10:45:03 INFO util.ShutdownHookManager: Deleting directory /tmp/spark-dd18587e-5244-4b4e-9eb1-f147c18f1306
21/04/13 10:45:03 INFO util.ShutdownHookManager: Deleting directory /tmp/spark-5904579a-d4c0-4a0a-8ffe-e7af09931299/pyspark-16b22e0d-ced4-4f30-955e-c0601e06c1ac
21/04/13 10:45:03 INFO util.ShutdownHookManager: Deleting directory /tmp/spark-5904579a-d4c0-4a0a-8ffe-e7af09931299
[root@hp2 pyspark]# 

2.3.3 RDD操作

RDDs支持兩種類型的操作:一種是轉換(transformations), 該操作從已有數據集創建新的數據集;另外一種是動作(actions),該操作在數據集上執行計算之后返回一個值給驅動程序。例如, map就是一個轉換,這個操作在數據集的每個元素上執行一個函數并返回一個處理之后新的RDD結果。另一方面,reduce是一個動作,這個操作按照某個函數規則聚集RDD中的所有元素并且把最終結果返回給驅動程序。

Spark中的所有轉換操作都是lazy模式的,也就是說,不是立馬做轉換計算結果,而是將這些轉換操作記錄在相應的數據集上,當需要通過動作(action)把結果返回給驅動程序時才真正執行。這個設計使Spark運行起來更加高效。例如,如果通過map創建的數據集后續會被reduce用到,那么只有reduce的結果會返回給驅動程序,而不是更大的map結果。默認情況下,RDD上的轉換操作在每次做動作時,都會重新執行計算一次。然而,我們可以使用persist(或者cache)函數將RDD存放在內存中,方便后續的快速訪問。另外,Spark也支持將RDD存放在磁盤上,或者在多個節點冗余存儲。

常見Transformations操作

Transformation 含義
map(func) 對每個RDD元素應用func之后,構造成新的RDD
filter(func) 對每個RDD元素應用func, 將func為true的元素構造成新的RDD
flatMap(func) 和map類似,但是flatMap可以將一個輸出元素映射成0個或多個元素。(也就是說func返回的是元素序列而不是單個元素).
mapPartitions(func) 和map類似,但是在RDD的不同分區上獨立執行。所以函數func的參數是一個Python迭代器,輸出結果也應該是迭代器【即func作用為Iterator<T> => Iterator】
mapPartitionsWithIndex(func) 和mapPartitions類似, but also provides func with an integer value representing the index of the partition, 但是還為函數func提供了一個正式參數,用來表示分區的編號。【此時func作用為(Int, Iterator<T>) => Iterator 】
sample(withReplacement, fraction, seed) 抽樣: fraction是抽樣的比例0~1之間的浮點數; withRepacement表示是否有放回抽樣, True是有放回, False是無放回;seed是隨機種子。
union(otherDataset) 并集操作,重復元素會保留(可以通過distinct操作去重)
intersection(otherDataset) 交集操作,結果不會包含重復元素
distinct([numTasks])) 去重操作
groupByKey([numTasks]) 把Key相同的數據放到一起【(K, V) => (K, Iterable<V>)】,需要注意的問題:1. 如果分組(grouping)操作是為了后續的聚集(aggregation)操作(例如sum/average), 使用reduceByKey或者aggregateByKey更高效。2.默認情況下,并發度取決于分區數量。我們可以傳入參數numTasks來調整并發任務數。
reduceByKey(func, [numTasks]) 首先按Key分組,然后將相同Key對應的所有Value都執行func操作得到一個值。func必須是(V, V) => V'的計算操作。numTasks作用跟上面提到的groupByKey一樣。
sortByKey([ascending], [numTasks]) 按Key排序。通過第一個參數True/False指定是升序還是降序。
join(otherDataset, [numTasks]) 類似SQL中的連接(內連接),即(K, V) and (K, W) => (K, (V, W)),返回所有連接對。外連接通過:leftOUterJoin(左出現右無匹配為空)、rightOuterJoin(右全出現左無匹配為空)、fullOuterJoin實現(左右全出現無匹配為空)。
cogroup(otherDataset, [numTasks]) 對兩個RDD做groupBy。即(K, V) and (K, W) => (K, Iterable<V>, Iterable(W))。別名groupWith。
pipe(command, [envVars]) 將驅動程序中的RDD交給shell處理(外部進程),例如Perl或bash腳本。RDD元素作為標準輸入傳給腳本,腳本處理之后的標準輸出會作為新的RDD返回給驅動程序。
coalesce(numPartitions) 將RDD的分區數減小到numPartitions。當數據集通過過濾減小規模時,使用這個操作可以提升性能。
repartition(numPartitions) 將數據重新隨機分區為numPartitions個。這會導致整個RDD的數據在集群網絡中洗牌。
repartitionAndSortWithinPartitions(partitioner) 使用partitioner函數充分去,并在分區內排序。這比先repartition然后在分區內sort高效,原因是這樣迫使排序操作被移到了shuffle階段。

常見Actions操作

Action 含義
reduce(func) 使用func函數聚集RDD中的元素(func接收兩個參數返回一個值)。這個函數應該滿足結合律和交換律以便能夠正確并行計算。
collect() 將RDD轉為數組返回給驅動程序。這個在執行filter等操作之后返回足夠小的數據集是比較有用。
count() 返回RDD中的元素數量。
first() 返回RDD中的第一個元素。(同take(1))
take(n) 返回由RDD的前N個元素組成的數組。
takeSample(withReplacement, num, [seed]) 返回num個元素的數組,這些元素抽樣自RDD,withReplacement表示是否有放回,seed是隨機數生成器的種子)。
takeOrdered(n, [ordering]) 返回RDD的前N個元素,使用自然順序或者通過ordering函數對將個元素轉換為新的Key.
saveAsTextFile(path) 將RDD元素寫入文本文件。Spark自動調用元素的toString方法做字符串轉換。
saveAsSequenceFile(path)(Java and Scala) 將RDD保存為Hadoop SequenceFile.這個過程機制如下:1. Pyrolite用來將序列化的Python RDD轉為Java對象RDD;2. Java RDD中的Key/Value被轉為Writable然后寫到文件。
countByKey() 統計每個Key出現的次數,只對(K, V)類型的RDD有效,返回(K, int)詞典。
foreach(func) 在所有RDD元素上執行函數func。
image.png

上圖是關于Transformations和Actions的一個介紹圖解,可以發現,Transformations操作過后的RDD依舊是RDD,而Actions過后的RDD都是非RDD。

2.3.3.1 RDD的map操作

求txt文檔文本總長度

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pyspark import SparkContext, SparkConf

# 設置Spark程序運行的地方,此處設置運行在本地模式,啟動2個線程分析數據
spark_conf = SparkConf().setMaster("local[*]").setAppName("Get Idcard")
sc = SparkContext(conf = spark_conf)

filename = 'file:///home/pyspark/idcard.txt'
rdd = sc.textFile(filename)

# 求每一行的長度
rdd_length = rdd.map(lambda s: len(s))
# 求所有行的長度
totalLength = rdd_length.reduce(lambda a, b: a + b)

print(totalLength)
sc.stop()

2.3.3.1 RDD使用函數

上一列,只是將len函數封裝成一個函數

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pyspark import SparkContext, SparkConf

# 定義一個函數,求長度
def myFunc(s):
    return len(s)

# 設置Spark程序運行的地方,此處設置運行在本地模式,啟動2個線程分析數據
spark_conf = SparkConf().setMaster("local[*]").setAppName("Get Idcard")
sc = SparkContext(conf = spark_conf)

filename = 'file:///home/pyspark/idcard.txt'
rdd = sc.textFile(filename)

# 求每一行的長度
rdd_length = rdd.map(myFunc)
# 求所有行的長度
totalLength = rdd_length.reduce(lambda a, b: a + b)

print(totalLength)
sc.stop()

參考:

1.http://spark.apache.org/docs/latest/rdd-programming-guide.html
2.https://www.modb.pro/db/45929
3.https://gourderwa.blog.csdn.net/article/details/104350323
4.http://spark.apache.org/docs/latest/api/python/reference/pyspark.html#rdd-apis
5.http://www.lxweimin.com/p/321034864bdb/
6.https://vlambda.com/wz_7iNyAJSkOIK.html

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容