累加器
提供了将工作节点中的值聚合到启动器程序中的简单语法。常见用途是调试时对作业执行过程中的事件进行计数。
# 例如累加空行
file = sc.textFile(inputFile)
# 创建累加器并初始化为0
blankLines = sc.accumulator(0)
def extractCallSigns(line):
global blankLines
if line == '':
blankLines += 1
return line.split(' ')
callSigns = file.flatMap(extractCallSigns)
callSigns.saveAsTextFile('')
print(blankLines)
广播变量
可以让程序高效的向所有工作节点发送一个较大的只读值
signPrefixes = sc.broadcast(loadCallSignTable())
def processSignCount(sign_count, signPrefixes):
country = lookupCountry(sign_count[0], signPrefixes.value)
count = sign_count[1]
return(country, count)
countryContactCounts = (contactCounts
.map(processSignCount)
.reduceByKey((lambda x,y: x+y)))
countryContactCounts.saveAsTextFile(outpuDir + "/countries.txt")
- 通过sparkcontext.broadcast创建
- 通过value属性访问
- 变量只会被发到各个节点各一次
note:可以使用spark.serializer属性选择另外一个序列化库来优化序列化过程
基于分区进行操作
基于分区对数据进行操作可以让我们避免为每个元素进行重复的配置工作,诸如打开数据库连接或创建随机数生成器等操作,都是我们应该避免为每一个元素进行的配置操作。
# 创建共享连接池
def processCallSign(signs):
# 创建连接池
http = urllib3.PoolManager()
# 与每一条呼号记录相关联的URL
urls = map(lambda x: "http://73s.com/qsos/%s.json"% x, signs)
# 创建请求(非阻塞)
request = map(lambda x: (x, http.request('GET', X)), urls)
# 获取结果
result = map(lambda x: (x[0], json.loads(x[1].data)), requests)
# 删除空的结果并返回
return filter(lambda x: x[1] is not None, result)
def fetchCallSigns(input):
# 获取呼号
return input.mapPartitions(lambda callSign: processCallSigns(callSigns))
conractsContactList = fetchCallSigns(validSigns)
使用mapPartitions()避免创建对象的开销
# 计算平均值
# 不使用
def combineCtrs(c1, c2):
return (c1[0] + c2[0], c1[1] + c2[1])
def basicAvg(nums):
num.map(lambda num: (num, 1)).reduce(combineCtrs)
# 使用
def partitionCtr(nums):
sumCount = [0,0]
for num in nums:
sumCount[0] += num
sumCount[1] += 1
return [sumCount]
def fastAvg(nums):
sumCount = num.mapPartitions(partitionCtr).reduce(combineCtrs)
return sumCount[0]/float(sumCount[1])
与外部程序间的管道
如果spark支持的语言不能实现开发要求,spark提供了一种通用机制,可以将数据通过管道传递给用其他语言编写的程序,比如R语言等等。
spark在RDD上提供了pipe()方法,pipe()方法可以让我们使用任意以重语言实现spark作业的部分逻辑,只要它可以读取linux标准流。
使用pipe()调用R驱动器程序
distScript = './src/R/finddistance.R'
distScriptName = 'finddistance.R'
sc.addFile(distScript)
def hasDistInfo(call):
'验证一次呼叫是否有计算距离时所必须的字段'
requiredFields = ['mylat', 'mylot', 'contactlat', 'contactlot']
return all(map(lambda f: call[f], requiredFilelds))
def formatCall(call):
'将数据格式重新组织使之可以被R程序解析'
return '{0},{1},{2},{3}'.format(
call['mylat'],call['mylot'],
call['contactlat'],call['contactlot'])
pipeInputs = contactsContactList.values().flatMap(
lambda calls: map(formatCall, filter(hasDistInfo, calls)))
distances = pipeInputs.pipe(SparkFiles.get(distScriptName))
print distances.collect()
数值RDD操作
count(),max(),min(),sum(),mean(),variance()~~~ 等等