筆記:新手的Spark指南

macOS Sierra 10.12.4

Spark 1.6.2

Python 2.7

轉載請注明出處:http://blog.csdn.net/MrLevo520/article/details/76087612

前言

既然做了Hive的整理,那就把spark的也整理下吧,當做入門指南和自己的筆記吧~與君共勉

Spark基礎

Spark是什么?

? Spark是UC Berkeley AMP lab所開源的類Hadoop MapReduce的通用分布式并行計算框架。Spark擁有hadoop MapReduce所具有的優點,但和MapReduce 的最大不同之處在于Spark是基于內存的迭代式計算——Spark的Job處理的中間輸出結果可以保存在內存中,從而不再需要讀寫HDFS,除此之外,一個MapReduce 在計算過程中只有map 和reduce 兩個階段,處理之后就結束了,而在Spark的計算模型中,可以分為n階段,因為它內存迭代式的,我們在處理完一個階段以后,可以繼續往下處理很多個階段,而不只是兩個階段。
  因此Spark能更好地適用于數據挖掘與機器學習等需要迭代的MapReduce的算法。其不僅實現了MapReduce的算子map 函數和reduce函數及計算模型,還提供更為豐富的算子,如filter、join、groupByKey等。是一個用來實現快速而同用的集群計算的平臺。— From Spark 工作原理及核心RDD 詳解

Spark原理過程

  1. 使用spark-submit提交一個Spark作業之后,這個作業就會啟動一個對應的Driver進程。

    • Driver:運行Application的main()函數并且創建SparkContext
  2. Driver根據我們設置的參數(比如說設定任務隊列,設定最大內存等)Cluster Manager 申請運行Spark作業需要使用的資源,這里的資源指的就是Executor進程,YARN集群管理器會根據我們為Spark作業設置的資源參數,在各個工作節點上,啟動一定數量的Executor進程,每個Executor進程都占有一定數量的內存和CPU core。

    • Executor:是為某Application運行在Worker Node上的一個進程,該進程負責運行Task,并且負責將數據存在內存或者磁盤上,每個Application都有各自獨立的Executors
    • Cluster Manager:集群管理器,在集群上獲取資源的外部服務(例如:Local、Standalone、Mesos或Yarn等集群管理系統)
  3. 申請到了作業執行所需的資源之后,river進程會將我們編寫的Spark作業代碼分拆為多個stage,每個stage執行一部分代碼片段,并為每個stage創建一批task,然后將這些task分配到各個Executor進程中執行,一個stage的所有task都執行完畢之后,會在各個節點本地的磁盤文件中寫入計算中間結果,然后Driver就會調度運行下一個stage。下一個stage的task的輸入數據就是上一個stage輸出的中間結果。如此循環往復,直到將我們自己編寫的代碼邏輯全部執行完

    • task:最小的計算單元,負責執行一模一樣的計算邏輯(也就是我們自己編寫的某個代碼片段)

      ?

什么是RDD?

全稱為彈性分布式數據集;本質上,RDD是種編程抽象,代表可以跨機器進行分割的只讀對象集合。RDD可以從一個繼承結構(lineage)重建(因此可以容錯),通過并行操作訪問,可以讀寫HDFS或S3這樣的分布式存儲,更重要的是,可以緩存到worker節點的內存中進行立即重用。由于RDD可以被緩存在內存中,Spark對迭代應用特別有效,因為這些應用中,數據是在整個算法運算過程中都可以被重用。大多數機器學習和最優化算法都是迭代的,使得Spark對數據科學來說是個非常有效的工具。另外,由于Spark非常快,可以通過類似Python REPL的命令行提示符交互式訪問。

RDD特點

  • RDD在抽象上來說是一種元素集合,包含了數據。它是被分區的,分為多個分區,每個分區分布在集群中的不同節點上,從而讓RDD中的數據可以被并行操作。(分布式數據集)
  • RDD的數據默認情況下存放在內存中的,但是在內存資源不足時,Spark會自動將RDD數據寫入磁盤。比如每個節點最多放5萬數據,結果你每個partition是10萬數據。那么就會把partition中的部分數據寫入磁盤上,進行保存。(彈性)
  • RDD將操作分為兩類:transformation與action。無論執行了多少次transformation操作,RDD都不會真正執行運算,只有當action操作被執行時,運算才會觸發。而在RDD的內部實現機制中,底層接口則是基于迭代器的,從而使得數據訪問變得更高效,也避免了大量中間結果對內存的消耗。
  • RDD最重要的特性就是,提供了容錯性,可以自動從節點失敗中恢復過來。即如果某個節點上的RDD partition,因為節點故障,導致數據丟了,那么RDD會自動通過自己的數據來源重新計算該partition。這一切對使用者是透明的。

RDD在Spark中的地位及作用

這需要從四個方面闡述

  • 為什么會有Spark?

    因為傳統的并行計算模型無法有效的解決迭代計算(iterative)和交互式計算(interactive);而Spark的使命便是解決這兩個問題,這也是他存在的價值和理由。

  • Spark如何解決迭代計算?

    其主要實現思想就是RDD,把所有計算的數據保存在分布式的內存中。迭代計算通常情況下都是對同一個數據集做反復的迭代計算,數據在內存中將大大提升IO操作。這也是Spark涉及的核心:內存計算。

  • Spark如何實現交互式計算?

    因為Spark是用scala語言實現的,Spark和scala能夠緊密的集成,所以Spark可以完美的運用scala的解釋器,使得其中的scala可以向操作本地集合對象一樣輕松操作分布式數據集。當然你也可以使用python,java,R等接口,spark也提供了相應的操作方式

  • Spark和RDD的關系?

    可以理解為:RDD是一種具有容錯性基于內存的集群計算抽象方法,Spark則是這個抽象方法的實現。

如何操作RDD?

Step1-獲取RDD

  • 自己創建個RDD,如以下語句:rdd = sc.parallelize(['1,2,3,4','5,6,6','9,10,11'])
  • 從共享的文件系統獲取,(如:HDFS)
  • 通過已存在的RDD轉換
  • 將已存在scala集合(只要是Seq對象)并行化,通過調用SparkContext的parallelize方法實現
  • 改變現有RDD的之久性;RDD是懶散,短暫的.(RDD的固化:cache緩存至內錯;save保存到分布式文件系統)

Step2-操作RDD

Transformation:根據數據集創建一個新的數據集,計算后返回一個新RDD;例如:Map將數據的每個元素經過某個函數計算后,返回一個新的分布式數據集即RDD。

值得注意的是,RDD的轉化操作都是惰性求值得,也就意味著在被調用行動操作之前Spark不會開始計算,相反,Spark會在內部記錄下所要求執行的操作的相關信息,因此在調用sc.textFile()時候,數據并沒有讀取進來,而是在必要的時候才會進行讀取。所以也就導致了導入文件的時候感覺很快的錯覺

  • Transformation的一些例子
image
def func(a):
    line_split = a.split(",")
    return sum(map(int,line_split))

data = sc.parallelize(['1,2,3,4','5,6,6','9,10,11'])  # 生成rdd
t_rdd= data.map(func)  # rdd的Transformation過程
a_rdd = t_rdd.collect()  # action過程  [10, 17, 30]

Actions:對數據集計算后返回一個數值value給驅動程序;例如:Reduce將數據集的所有元素用某個函數聚合后,將最終結果返回給程序。返回的是一個新的數據類型,這里注意的是,返回的并不是新的RDD,只有Transformation之后是新的RDD

  • Actions具體內容
image

spark執行步驟

  1. 定義一個或多個RDD,可以通過獲取存儲在磁盤上的數據(HDFS,Cassandra,HBase,Local Disk),并行化內存中的某些集合,轉換(transform)一個已存在的RDD,或者,緩存或保存。
  2. 通過傳遞一個閉包(函數)給RDD上的每個元素來調用RDD上的操作。Spark提供了除了Map和Reduce的80多種高級操作。
  3. 使用結果RDD的動作(action)(如count、collect、save等)。動作將會啟動集群上的worker機器進行計算。

當Spark在一個worker上運行閉包時,閉包中用到的所有變量都會被拷貝到節點上,但是由閉包的局部作用域來維護。Spark提供了兩種類型的共享變量,這些變量可以按照限定的方式被所有worker訪問。廣播變量會被分發給所有worker,但是是只讀的。累加器這種變量,worker可以使用關聯操作來“加”,通常用作計數器。

Spark實際操作

那么, sc的是什么鬼?

你可以把他理解成由SparkContext構造出來的實例,通過這個實例我們可以構造自己的RDD

# -*- coding:utf-8 -*-
from pyspark import SparkContext, SparkConf
from pyspark.streaming import StreamingContext
import math
appName ="hellospark" #你的應用程序名稱
master= "local"#設置單機
conf = SparkConf().setAppName(appName).setMaster(master)#配置SparkContext
sc = SparkContext(conf=conf)

# 一個簡單的wordcount測試
str_ = '''this is a word count test only test show twice'''
data = sc.parallelize(str_.split(" "))
data.map(lambda x:(x,1)).reduceByKey(lambda x,y:x+y).collect()

# spark.akka.frameSize: 控制Spark中通信消息的最大容量 (如 task 的輸出結果),默認為10M。當處理大數據時,task 的輸出可能會大于這個值,需要根據實際數據設置一個更高的值。
# SparkConf為Spark配置類,配置已鍵值對形式存儲,封裝了一個ConcurrentHashMap類實例settings用于存儲Spark的配置信息;配置項包括:master、appName、Jars、ExecutorEnv等等
# SparkContext用于連接Spark集群、創建RDD、累加器(accumlator)、廣播變量(broadcast variables),所以說SparkContext為Spark程序的根

注意sc的構造是怎么來的

方法一:在jupyter中操作(推薦)

當然,你得把pyspark的kernel配到jupyter中,可參考解決:win遠程連接ubuntu服務器安裝jupyter,啟動pyspark

這里寫圖片描述

方法二:使用spark-submit pythonfile.py來實現提交python腳本操作

# 前提是在一個文件夾中,不然要定位文件位置
$ spark-submit --driver-memory 6G --queue 如果有隊列填上隊列名字 testpy.py 可帶參數

# pyspark test
# 中文測試

方法三:使用ipython在pyspark的shell中操作

# 啟動local spark:pyspark --master local[2]
# local[2]是開雙核的意思,[4]即是開4核

xiaoju@map-traffic-spd131.gz01:~$ pyspark --master local[2]

In [8]: line = sc.textFile("file:/home/xiaoju/user/xukai/test.tx #創建RDD載入的路徑這里是機器路徑
   ...: t")

In [9]: pythonlines = line.filter(lambda line:"test" in  line) # 轉化操作

In [10]: pythonlines.first() # 行動操作
Out[10]: u'test;'

# 當一個文本讀取為RDD時,輸入的每一行都會成為RDD的一個元素
In [21]: line.first()
Out[21]: u'this is a test txtfile!'

In [24]: print line.first().split(" ")[0] # 這樣就可以流暢使用python進行操作了,只是導入的時候用的是RDD存儲
this

In [26]: stringlist = line.first().split(" ")
In [27]: nums = sc.parallelize(stringlist) # 用sparkContext的parallelize制作RDD的,是ParallelCollectionRDD,創建一個并行集合。

In [28]: squared = nums.map(lambda x:x=="this").collect()

In [29]: for num in squared:
    ...:     print num
    ...:
True
False
False
False
False

In [34]: words = lines.flatMap(lambda line:line.split(" ")).collec
    ...: t()  # 使用collece()才能進行for輸出,flatmap文件中的所有行數據僅返回了一個數組對象

In [35]: for i in words:
    ...:     print i
    ...:
this
is
a
test

# 產生新的鍵值對pair類型RDD
In [56]: rdd = sc.parallelize([1,2,3,3])

# 操作過程中,轉化并不會被執行,需要有個動作操作才被執行,比如collect()
In [57]: rdd.collect()
Out[57]: [1, 2, 3, 3]
In [59]: rdd1 = rdd.map(lambda x:(x,x+1))

In [60]: rdd1.collect()
Out[60]: [(1, 2), (2, 3), (3, 4), (3, 4)]

In [64]: rdd2 = rdd1.filter(lambda x: x[0]>2)

In [65]: rdd2.collect()
Out[65]: [(3, 4), (3, 4)]

In [67]: rdd1.sortByKey().collect()
Out[67]: [(1, 2), (2, 3), (3, 4), (3, 4)]

一些Demo

# -*- coding:utf-8 -*-
# 如果使用jupyter的話,sc已經構造好了,不需要再倒入包
from pyspark import SparkContext, SparkConf
from pyspark.streaming import StreamingContext
import math
appName ="hellospark" #你的應用程序名稱
master= "local"#設置單機
conf = SparkConf().setAppName(appName).setMaster(master)#配置SparkContext
sc = SparkContext(conf=conf)
 
# parallelize:并行化數據,轉化為RDD
# 并行集合的一個重要參數是slices,表示數據集切分的份數。Spark將會在集群上為每一份數據起一個任務。
# 典型地,你可以在集群的每個CPU上分布2-4個slices. 一般來說,Spark會嘗試根據集群的狀況,
# 來自動設定slices的數目。
data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data, numSlices=10)  # numSlices為分塊數目,根據集群數進行分塊
 


#--------------------------------------------------
# textFile讀取外部數據
rdd = sc.textFile("file:/data/map_da/xukai/sparkstreaming/test/test.txt")  # 以行為單位讀取外部文件,并轉化為RDD
print rdd.collect()

#  打印出的結果是  [u'lslsllslsiiiiiiiiiii']

#--------------------------------------------------

# map:迭代,對數據集中數據進行單獨操作
def my_add(l):
    return (l,l)
data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)  # 并行化數據集
result = distData.map(my_add)
print (result.collect())  # 返回一個分布數據集

# 打印出的結果  [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]

#--------------------------------------------------

# filter:過濾數據
def my_add(l):
    result = False
    if l > 2:
        result = True
    return result
data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)#并行化數據集,分片
result = distData.filter(my_add)
print (result.collect())#返回一個分布數據集
 

# [3, 4, 5]
# zip:將兩個RDD對應元素組合為元組

#--------------------------------------------------

x = sc.parallelize(range(0,5))
y = sc.parallelize(range(1000, 1005))
print x.zip(y).collect()
 
# [(0, 1000), (1, 1001), (2, 1002), (3, 1003), (4, 1004)]
 
 
 
#union 組合兩個RDD
print x.union(x).collect()
# [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
# Aciton操作

#--------------------------------------------------
 
# collect:返回RDD中的數據
rdd = sc.parallelize(range(1, 10))
print rdd
print rdd.collect()

# ParallelCollectionRDD[11] at parallelize at PythonRDD.scala:423
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

#--------------------------------------------------

# collectAsMap:以rdd元素為元組,以元組中一個元素作為索引返回RDD中的數據
m = sc.parallelize([('a', 2), (3, 4)]).collectAsMap()
print m['a']
print m[3]

#2
#4


#--------------------------------------------------

# groupby函數:根據提供的方法為RDD分組:
rdd = sc.parallelize([1, 1, 2, 3, 5, 8])
def fun(i):
    return i % 2
result = rdd.groupBy(fun).collect()
print [(x, sorted(y)) for (x, y) in result]
 
# [(0, [2, 8]), (1, [1, 1, 3, 5])]

#--------------------------------------------------

# reduce:對數據集進行運算
rdd = sc.parallelize(range(1, 10))
result = rdd.reduce(lambda a, b: a + b)
print result
# 45

#--------------------------------------------------

a = sc.parallelize([i for i in range(9)], 3)
print a.collect()
#[0, 1, 2, 3, 4, 5, 6, 7, 8]

y = a.map(lambda a:(a,a*2))  # 需要的表現形式為(a,a*2)的形式,而a是傳遞的參數
print y.collect()
#[(0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14), (8, 16)]

z = a.map(lambda a:a*2)
print print z.collect()
#[0, 2, 4, 6, 8, 10, 12, 14, 16]

y = a.flatMap(lambda a:(a*2,a*3))
print y.collect();
#[0, 0, 2, 3, 4, 6, 6, 9, 8, 12, 10, 15, 12, 18, 14, 21, 16, 24]


#--------------------------------------------------
# union有點像append

x = sc.parallelize(['A','A','B'])
y = sc.parallelize(['D','C','A'])
z = x.union(y)
z2 = x.intersection(y)
print(x.collect())
#['A', 'A', 'B']
print(y.collect())
#['D', 'C', 'A']
print(z.collect())
#['A', 'A', 'B', 'D', 'C', 'A']
print(z2.collect())
# ['A']




#--------------------------------------------------

x = sc.parallelize([1,2,3])
y = x.groupBy(lambda x: 'A' if (x%2 == 1) else 'B' )
print(x.collect())
#[1, 2, 3]
print([(j[0],[i for i in j[1]]) for j in y.collect()])
#[('A', [1, 3]), ('B', [2])]

#--------------------------------------------------

x=sc.parallelize([1,3,1,2,3])
y=x.countByValue()
print y[1]
#2

#--------------------------------------------------

# 按升序排,取前n個
x = sc.parallelize([1,3,1,2,3,4,1,6])
y=x.takeOrdered(5)
# [1, 1, 1, 2, 3]

小結

  1. 總的來說,RDD之所以被描述為"彈性",是因為在任何時候都能進行重算,因為保存RDD數據的一臺機器失敗時,Spark可以使用這種特性來重算出丟棄的部分分區。
  2. 轉化RDD的時候,是返回新的RDD而不是對現有的RDD進行操作,只有在執行動作的時候返回的是其他數據類型 。

Spark進階

這里會總結下我以前實習時候用到過的一些處理方法

使用MySqldb+Pyspark操作Mysql

  1. 首先得知道,這個數據庫在哪,也就是數據庫所在服務器的ip地址,才能進行連接
# 使用ping命令進行所需要連接的數據庫的ip地址獲取

$ ping 服務器
PING xxxxxx bytes of data.
64 bytes from xxxxxx: icmp_seq=1 ttl=64 time=0.020 ms
  1. 使用Mysqldb包進行數據庫的連接操作
In [22]: import MySQLdb
In [23]: conn = MySQLdb.connect(host=ip地址,user=用戶名
    ...: ,passwd=密碼,db='test',charset='utf8')
# 這邊連接的時候最好制定數據庫,即添加 db="test",charset="utf8",如不制定,則在sql語句中選擇上指定的數據庫名字

In [24]: cursor = conn.cursor()

In [25]: count = cursor.execute("select count(*) from test_uk ")

In [26]: print cursor.fetchall()
((8L,),)


# 打開服務器上的Mysql查看一下,ok,沒問題,獲取行數正確

mysql> select * from test_uk;
+----+----+-------+
| id | tp | value |
+----+----+-------+
|  3 |  1 |  0.75 |
|  4 |  5 |     0 |
|  5 |  6 |     2 |
|  6 |  4 |     0 |
|  9 |  7 |     3 |
| 11 |  9 |     2 |
| 12 | 10 |    11 |
| 14 | 13 |    13 |
+----+----+-------+
8 rows in set (0.00 sec)

# 寫入test數據庫中的tbl_realtime_statis表,記得需要提交

mysql> desc tbl_realtime_statis

+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
| value | double  | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# 開始執行insert動作

In [38]: cursor.execute('INSERT INTO tbl_realtime_statis (id,value) VALUE
    ...: S (1,11)')
Out[38]: 1L

In [39]: cursor.connection.commit()

mysql> select * from tbl_realtime_statis;
+------+-------+
| id   | value |
+------+-------+
|    1 |    11 |
+------+-------+
1 row in set (0.00 sec)

更多語句參考:python使用mysqldb連接數據庫操作方法示例詳解

使用spark-submit pythonfile來執行本地txt寫入指定數據庫的操作

在文件同目錄下創建名為txttosql.py的python腳本,填寫如下

conn = MySQLdb.connect(host='',user='',passwd='',db='test',charset='utf8')
cursor = conn.cursor()

datapath = "test.txt"
with open(datapath) as f:
    for line in f.readlines():
        linesplit = line.strip().split(",")
        key = int(linesplit[0])
        value = int(linesplit[1])
        sqlstring = 'INSERT INTO tbl_realtime_statis (id,value) VALUES (%d,%d)'%(key,value)
        cursor.execute(sqlstring)
        cursor.connection.commit()

cursor.close()
conn.close()

之后執行spark-submit txttosql.py即可,注意數據庫如果已有數據,將不會被覆蓋,而是之后插入操作

使用JDBC+Pypark進行MySql操作

建一個parallelize的RDD

In [12]: rdd1 = sc.parallelize([(1,'id1',100000,12,1.2),(2,'id2',2000000,13,1.22)])

轉化成為DataFrame的RDD

In [13]: rdd2 = rdd1.toDF()
# 會耗費比較長的時間

In [14]: rdd2.collect()
Out[14]:
[Row(_1=1, _2=u'id1', _3=100000, _4=12, _5=1.2),
 Row(_1=2, _2=u'id2', _3=2000000, _4=13, _5=1.22)]

In [15]: rdd2.show()
+---+---+-------+---+----+
| _1| _2|     _3| _4|  _5|
+---+---+-------+---+----+
|  1|id1| 100000| 12| 1.2|
|  2|id2|2000000| 13|1.22|
+---+---+-------+---+----+

In [18]: rdd2.filter("_3 > 100000").show()
+---+---+-------+---+----+
| _1| _2|     _3| _4|  _5|
+---+---+-------+---+----+
|  2|id2|2000000| 13|1.22|
+---+---+-------+---+----+

# 可以修改別名,貌似只有一次改的?
In [34]: rdd2.withColumnRenamed("_2","name_string").withColumnRenamed("_3","money_bigint")
Out[34]: DataFrame[_1: bigint, name_string: string, money_bigint: bigint, _4: bigint, _5: double]


# 嘗試在toDF的時候就寫好名字

In [41]: rdd3 = rdd1.toDF(["id_int","name_string","money_bigint","age_double","tall_float"])

In [42]: rdd3.write.jdbc("jdbc:mysql://xxxx/test", "testalltype", "overwrite", {"
    ...: user":"", "password":""})
    
# 查看

mysql> select * from testalltype;
+--------+-------------+--------------+------------+------------+
| id_int | name_string | money_bigint | age_double | tall_float |
+--------+-------------+--------------+------------+------------+
|      2 | id2         |      2000000 |         13 |       1.22 |
|      1 | id1         |       100000 |         12 |        1.2 |
+--------+-------------+--------------+------------+------------+
2 rows in set (0.00 sec)


# 插入語句可以用append,使用另一種方法創建dataframe

In [48]: newline = [(932,'Alice', 1929291,2,22.92)]
In [51]: rdd4 = sqlContext.createDataFrame(newline,['id_int','name
    ...: _string','money_bigint','age_double','tall_float'])

In [52]: rdd4.show()
+------+-----------+------------+----------+-----------+
|id_int|name_string|money_bigint|age_double|tall_float|
+------+-----------+------------+----------+-----------+
|   932|      Alice|     1929291|         2|      22.92|
+------+-----------+------------+----------+-----------+


In [55]: rdd4.write.jdbc("jdbc:mysql://xxxx/test","t
    ...: estalltype","append",{"user":"","password":"
    ...: "})

# 查看

mysql> select * from testalltype;
+--------+-------------+--------------+------------+------------+
| id_int | name_string | money_bigint | age_double | tall_float |
+--------+-------------+--------------+------------+------------+
|      2 | id2         |      2000000 |         13 |       1.22 |
|      1 | id1         |       100000 |         12 |        1.2 |
|    932 | Alice       |      1929291 |          2 |       22.92 |
+--------+-------------+--------------+------------+-------------+



##############使用pyspark+jdbc將本地csv存儲到mysql###########


In [1]: datapath = "dataform.csv"

In [2]: with open(datapath) as f:
   ...:     k = 1
   ...:     parallelizelist = []
   ...:     for line in f.readlines():
   ...:         linesplit = line.strip().split("|")
   ...:         tuple_data = tuple(linesplit)
   ...:         if k == 1:
   ...:             tuple_title = linesplit
   ...:         else:
   ...:             parallelizelist.append(tuple_data)
   ...:
   ...:         k +=1

# 方法1:sqlContext.createDataFrame

In [3]: rdd3 = sqlContext.createDataFrame(parallelizelist,tuple_title)

In [4]: rdd3.write.jdbc("jdbc:mysql://xxxx/test","testallty
   ...: pe","overwrite",{"user":"","password":"
   ...: "})


# 方法2:toDF
In [8]: rdd4 = sc.parallelize(parallelizelist)

In [9]: rdd5 = rdd4.toDF(tuple_title)

In [10]: rdd5.write.jdbc("jdbc:mysql://xxxx/test","testallt
    ...: ype","append",{"user":"","password":""
    ...: })

Spark對Hive表操作

首先理解下什么是SparkContext, SQLContext 和HiveContext,原文可參考@pig2--讓你真正理解什么是SparkContext, SQLContext 和HiveContext這位版主很厲害!這里簡單總結下

  • SparkContext:用于連接Spark集群、創建RDD、累加器(accumlator)、廣播變量(broadcast variables),所以說SparkContext為Spark程序的根,你只要知道它能讓一個普通的列表編程rdd就行了,非常牛逼,就是傳說中的sc!
  • SparkSQL:是spark的一個模塊,是spark的一個模塊,SparkSQL 用來處理結構化數據,所以SparkSQL你的data必須定義schema.在spark1.3.1,sparksql繼承dataframes 和SQL 查詢引擎
    • SQLContext:spark處理結構化數據的入口。允許創建DataFrame以及sql查詢
    • HiveContext:spark sql執行引擎,集成hive數據
In [68]: from pyspark.sql import HiveContext,Row

In [69]: hiveCtx = HiveContext(sc)

In [70]: rows = hiveCtx.sql("SELECT * FROM test.table1 limit
    ...: 5")

In [71]: firstRow = rows.first()
[Stage 32:=====> 
[Stage 32:========>
[Stage 32:==========>
[Stage 32:=============>
[Stage 32:================>
[Stage 32:==================>
[Stage 32:=====================>
[Stage 32:======================>
In [72]: print firstRow.business_id
257

In [73]: print firstRow.order_id
3057564118


In [74]: hiveowntest = HiveContext(sc)

In [75]: rows2 = hiveowntest.sql("SELECT * FROM test.owntest")

In [76]: rows2.show()
+--------+---+
|    name|age|
+--------+---+
|shangsan| 20|
|    lisi| 22|
|  zhouwu| 21|
+--------+---+

# 保存入表,其實就是講hive表讀入RDD,然后再寫入新的hive表中
In [79]: rows2.saveAsTable("hive_test_spark")


# 然后進入hive中進行操作,雖然有點錯誤,單還是可以執行查詢動作

hive> select * from hive_test_spark;
OK
hive_test_spark.name    hive_test_spark.age
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
shangsan    20
lisi    22
zhouwu  21

hive> select * from hive_test_spark where name="shangsan";
OK
hive_test_spark.name    hive_test_spark.age
shangsan    20
Time taken: 0.645 seconds, Fetched: 1 row(s)

Pyspark使用本地文件建立Hive表

1.再另一終端,執行如下,將文件put到集群

# 其中spark_write_hive.txt是本地寫好的文件,之后的操作是put到hdfs上

$ hadoop fs -put spark_write_hive.txt hdfs:/xxxx/xxxx/

# 查看是否put到hdfs上

$ hadoop fs -cat hdfs:/xxxx/xxxx/spark_write_hive.txt

shangsan,20
lisi,30

2.需要測試的文件已推送到集群上存儲,接下來是使用spark并將數據導入到表中

sqlContext = HiveContext(sc)

********
# 建hive表
sqlContext.sql("CREATE TABLE IF NOT EXISTS hive_test_spark2 (key INT,value STRING)")

# 導入數據,注意這里是集群的數據
hivenewtable.sql("LOAD DATA INPATH '/xxxx/xxxx/test.txt' INTO TABLE hive_test_spark2")

********
# 還是推薦以下方式建立外表
In [21]: txttohive = HiveContext(sc)

In [22]: txttohive.sql("CREATE EXTERNAL TABLE IF NOT EXISTS hive_t
    ...: est_spark3 (name string,age string ) ROW FORMAT DELIMITED
    ...:  FIELDS TERMINATED BY ','")
Out[22]: DataFrame[result: string]

In [23]: txttohive.sql("LOAD DATA INPATH '/xxxx/xxxx/
    ...: spark_write_hive.txt' INTO TABLE hive_test_s
    ...: park3")
16/12/22 13:49:35 ERROR KeyProviderCache: Could not find uri with key [dfs.encryption.key.provider.uri] to create a keyProvider !!
Out[23]: DataFrame[result: string]

# 查看hive表
hive> select * from hive_test_spark3;
OK
hive_test_spark3.name hive_test_spark3.age
shangsan    20
lisi    30

使用Pyspark 將Hive表轉化成rdd操作

from pyspark.sql import HiveContext,Row
hiveCtx = HiveContext(sc)
data_order = hiveCtx.sql("select * from xx.hot_position")
data_order.show(7)

+----+--------+-------+----+-----------+----+-----+---+
|city|  badlng| badlat| num|badcaseprec|year|month|day|
+----+--------+-------+----+-----------+----+-----+---+
| xx市|1x6.3213|x9.8959|3597|      0.857|2017|   05| 10|
| xx市| 1x6.379| x9.865|5775|      0.857|2017|   05| 10|
| xx市|1x1.3198|x1.1937|1269|      0.849|2017|   05| 10|
| xx市|1x1.3199|x1.1937|3387|      0.847|2017|   05| 10|
| xx市|1x6.5509|x9.6083|1092|      0.835|2017|   05| 10|
| xx市| 1x1.354|x1.1988|1482|      0.825|2017|   05| 10|
| xx市|1x0.2131|x0.2915|8215|      0.817|2017|   05| 10|
+----+--------+-------+----+-----------+----+-----+---+
only showing top 7 rows


# 將dataframe轉化為rdd進行計算
data_order_rdd=data_order.rdd
data_order_rdd.map(lambda x:(x.badlng,x.badlat)).collect()

# output
[(u'1x6.3213', u'x9.8959'),
 (u'1x6.379', u'x9.865'),
 (u'1x1.3198', u'x1.1937'),
 (u'1x1.3199', u'x1.1937'),
 (u'1x6.5509', u'x9.6083')]
 

當然你也可以這樣操作

# 進行復合計算
# -*- coding: utf-8 -*- 
from math import*
def Distance2(data):# 第二種計算方法
    lat2=float(data.split("\t")[1])
    lng2=float(data.split("\t")[0])
    lat1=39.8959
    lng1=116.3213
    radlat1=radians(lat1)  
    radlat2=radians(lat2)  
    a=radlat1-radlat2  
    b=radians(lng1)-radians(lng2)  
    s=2*asin(sqrt(pow(sin(a/2),2)+cos(radlat1)*cos(radlat2)*pow(sin(b/2),2)))  
    earth_radius=6378.137  
    s=s*earth_radius  
    if s<0:  
        return -s  
    else:  
        return s

data_order_rdd.filter(lambda x:x.badlat>'31').map(lambda x:x.badlng+'\t'+x.badlat).map(Distance2).collect()


# 輸出
[0.0,
 6.010588654903075,
 1068.8138888545056,
 1068.8177053251293,
 1069.6032160827797,
 1068.969082793839,
 0.07015355066273321]

Spark Streaming

Spark是一個類似于MapReduce的分布式計算框架,其核心是彈性分布式數據集,提供了比MapReduce更豐富的模型,可以在快速在內存中對數據集進行多次迭代,以支持復雜的數據挖掘算法和圖形計算算法。Spark Streaming是一種構建在Spark上的實時計算框架,它擴展了Spark處理大規模流式數據的能力。

image

基于云梯Spark on Yarn的Spark Streaming總體架構如圖,Spark on Yarn啟動后,由Spark AppMaster把Receiver作為一個Task提交給某一個Spark Executor;Receive啟動后輸入數據,生成數據塊,然后通知Spark AppMaster;Spark AppMaster會根據數據塊生成相應的Job,并把Job的Task提交給空閑Spark Executor 執行。圖中藍色的粗箭頭顯示被處理的數據流,輸入數據流可以是磁盤、網絡和HDFS等,輸出可以是HDFS,數據庫等。

Spark Streaming的基本原理

將輸入數據流以時間片(秒級)為單位進行拆分,然后以類似批處理的方式處理每個時間片數據,其基本原理如圖

image

首先,Spark Streaming把實時輸入數據流以時間片Δt (如1秒)為單位切分成塊。Spark Streaming會把每塊數據作為一個RDD,并使用RDD操作處理每一小塊數據。每個塊都會生成一個Spark Job處理,最終結果也返回多塊。

Spark Streaming的內部原理

使用Spark Streaming編寫的程序與編寫Spark程序非常相似,在Spark程序中,主要通過操作RDD(Resilient Distributed Datasets彈性分布式數據集)提供的接口,如map、reduce、filter等,實現數據的批處理。而在Spark Streaming中,則通過操作DStream(表示數據流的RDD序列)提供的接口,這些接口和RDD提供的接口類似。

image

Spark Streaming把程序中對DStream的操作轉換為DStream Graph

image

對于每個時間片,DStream Graph都會產生一個RDD Graph;針對每個輸出操作(如print、foreach等),Spark Streaming都會創建一個Spark action;對于每個Spark action,Spark Streaming都會產生一個相應的Spark job,并交給JobManager。JobManager中維護著一個Jobs隊列, Spark job存儲在這個隊列中,JobManager把Spark job提交給Spark Scheduler,Spark Scheduler負責調度Ta

Spark Streaming優缺點

優點

  1. Spark Streaming 內部的實現和調度方式高度依賴 Spark 的 DAG 調度器和 RDD,這就決定了 Spark Streaming 的設計初衷必須是粗粒度方式的,同時,由于 Spark 內部調度器足夠快速和高效,可以快速地處理小批量數據,這就獲得準實時的特性。
  2. Spark Streaming 的粗粒度執行方式使其確保“處理且僅處理一次”的特性,同時也可以更方便地實現容錯恢復機制。
  3. 由于 Spark Streaming 的 DStream 本質是 RDD 在流式數據上的抽象,因此基于 RDD 的各種操作也有相應的基于 DStream 的版本,這樣就大大降低了用戶對于新框架的學習成本,在了解 Spark 的情況下用戶將很容易使用 Spark Streaming。
  4. 由于 DStream 是在 RDD 上的抽象,那么也就更容易與 RDD 進行交互操作,在需要將流式數據和批處理數據結合進行分析的情況下,將會變得非常方便。

缺點

  1. Spark Streaming 的粗粒度處理方式也造成了不可避免的延遲。在細粒度處理方式下,理想情況下每一條記錄都會被實時處理,而在 Spark Streaming 中,數據需要匯總到一定的量后再一次性處理,這就增加了數據處理的延遲,這種延遲是由框架的設計引入的,并不是由網絡或其他情況造成的。
  2. Spark Streaming 當前版本穩定性不是很好。[spark 1.5]

如何使用Spark Streaming

可參考:Spark Streaming 集成 Kafka 總結

作為構建于Spark之上的應用框架,Spark Streaming承襲了Spark的編程風格,對于已經了解Spark的用戶來說能夠快速地上手。接下來以Spark Streaming官方提供的WordCount代碼為例來介紹Spark Streaming的使用方式。

// scala語言編寫spark streaming

import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.streaming.StreamingContext._
 
// Create a local StreamingContext with two working thread and batch interval of 1 second.
// The master requires 2 cores to prevent from a starvation scenario.
val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(1))
 
// Create a DStream that will connect to hostname:port, like localhost:9999
val lines = ssc.socketTextStream("localhost", 9999)
 
// Split each line into words
val words = lines.flatMap(_.split(" "))
import org.apache.spark.streaming.StreamingContext._
// Count each word in each batch
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKey(_ + _)
 
// Print the first ten elements of each RDD generated in this DStream to the console
wordCounts.print()
ssc.start()              // Start the computation
ssc.awaitTermination()  // Wait for the computation to terminate

Spark在機器學習中應用

第一步,新建python 文件,取名svmwithsgd.py]

# -*- coding:utf-8 -*-
from pyspark import SparkContext, SparkConf
from pyspark.streaming import StreamingContext
from pyspark.mllib.classification import SVMWithSGD, SVMModel
from pyspark.mllib.regression import LabeledPoint
appName ="hellospark" #你的應用程序名稱
master= "local"#設置單機
conf = SparkConf().setAppName(appName).setMaster(master)#配置SparkContext
sc = SparkContext(conf=conf)

# Load and parse the data
def parsePoint(line):
    values = [float(x) for x in line.strip().split(' ')]
    return LabeledPoint(values[0], values[1:])

data = sc.textFile("newdata.txt")
parsedData = data.map(parsePoint)

# Build the model
model = SVMWithSGD.train(parsedData, iterations=100)

# Evaluating the model on training data
labelsAndPreds = parsedData.map(lambda p: (p.label, model.predict(p.features)))
trainErr = labelsAndPreds.filter(lambda (v, p): v != p).count() / float(parsedData.count())
print("Training Error = " + str(trainErr))

# Save and load model
#model.save(sc, "myModelPath")
#sameModel = SVMModel.load(sc, "myModelPath")
# 最后兩段會加載出錯
#SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
#SLF4J: Defaulting to no-operation (NOP) logger implementation
#SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

第二步,進行spark-submit 提交任務

$ spark-submit svmwithsgd.py

Pyspark在工作流中套路

詳見我的另一篇博客Spark日志清洗一般流程


總結

在實習的過程中,對于不同的任務啟用不同的工具,處理手段,技巧等,而對編程語言的選擇,其實并不是那么重要,這是一種實現形式罷了,效率的核心還是對數據結構和算法的理解上,大數據處理在我現在的認知范圍內,只不過是一種海量數據處理的技術,就像一臺機器不夠算了,那就搞兩臺,n臺,機械硬盤算起來不夠快了,那就加載到內存中算(spark),當然機器之間的通信,任務的派發,最后的匯總這些也是非常值得琢磨的,而設置分配內存,設置mapreduce個數這類的,很多時候都是靠經驗來總結,對數據的把握程度,預估上進行判斷,如何更快速的處理數據并且開銷更低,這里都是屬于性能調優里面的。總之,能處理數據,了解t和a用法,并不能說掌握spark,只能說會用這個工具而已,而現實中大多數任務只是挑選工具的過程而已,我們通常是為了close掉任務而去學習一種新的更快的工具,我想這對于工具的理解和學習來說是十分不利的,而越來越多的任務迫使我們沒有太多的余力靜下心去去更深入的學習和了解,記錄和總結。而我,并不想這樣。


致謝

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

推薦閱讀更多精彩內容