spark shell 删除失效_Spark编程笔记(4)-RDD数据读写

前言

上一篇文章介绍了RDD编程中非常重要的键值对RDD编程方法。本小节按照原定计划,继续整理RDD编程章节中的后续内容。

⭐️前文提到,完整的RDD编程章节会分为4部分进行介绍:

  • RDD编程基础
  • 键值对RDD
  • 数据读写
  • 综合案例

截至目前,RDD编程基础键值对RDD已经介绍过了。本节首先介绍数据读写相关的内容,然后再通过几个综合案例,深化对RDD编程的三篇文章的理解 。

⭐️本文(RDD数据读写)目录如下:

前言
文件数据读写
读写HBase数据
RDD综合实例
总结

Part1.文件数据读写

⭐️可以从本地文件系统中读取数据。

1)从文件中读取数据创建RDD 。

>>> 

因为Spark采用了惰性机制,在执行转换操作的时候,即使输入了错误的语句, 假设word123.txt不存在,spark-shell也不会马上报错 。

>>> textFile = sc.
... textFile("file:///usr/local/spark/mycode/wordcount/word123.txt")

2)把RDD写入到文本文件中。

>>> textFile = sc.
... textFile("file:///usr/local/spark/mycode/rdd/word.txt")
>>> textFile.
... saveAsTextFile("file:///usr/local/spark/mycode/rdd/writeback")

⭐️如果想再次把数据加载在RDD中,只要使用writeback这个目录 :

>>> textFile = sc.
... textFile("file:///usr/local/spark/mycode/rdd/writeback")

⭐️可以从分布式文件系统HDFS中读写数据。

从分布式文件系统HDFS中读取数据,也是采用textFile()方法,可以为textFile() 方法提供一个HDFS文件或目录地址,如果是一个文件地址,它会加载该文件, 如果是一个目录地址,它会加载该目录下的所有文件的数据。如下三条语句都是等价的:

>>> textFile = sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt")
>>> textFile = sc.textFile("/user/hadoop/word.txt")
>>> textFile = sc.textFile("word.txt")

⭐️同样,可以使用saveAsTextFile()方法把RDD中的数据保存到HDFS文件中,命 令如下:

>>> textFile = sc.textFile("word.txt")
>>> textFile.saveAsTextFile("writeback")

Part3.读写HBase数据

HBase是Google BigTable的开源实现。之所以叫做BigTable,是因为他是一个很大很大的表。每个值是一个未经解释的字符串,没有数据类型。但与关系型数据库不同。HBase是一个稀疏、多维度、排序的映射表 。这张表的索引是行键列族列限定符时间戳。用户在表中存储数据,每一行都有一个可排序的行键和任意多的列。表在水平方向由一个或者多个列族组成,一个列族中可以包含任意多 个列,同一个列族里面的数据存储在一起。

列族支持动态扩展 ,可以很轻松地添加一个列族或列,无需预先定义 列的数量以及类型,所有列均以字符串形式存储,用户需要自行进行 数据类型转换 。HBase中执行更新操作时,并不会删除数据旧的版本,而是生成一个 新的版本,旧有的版本仍然保留 (这是和HDFS只允许追加不允许修 改的特性相关的)。

⭐️分别解析一下HBase的索引:

  • 表:HBase采用表来组织数据,表由行 和列组成,列划分为若干个列族 ;
  • 行:每个HBase表都由若干行组成,每 个行由行键(row key)来标识;
  • 列族:一个HBase表被分组成许多“列 族”(Column Family)的集合,它是 基本的访问控制单元;
  • 列限定符:列族里的数据通过列限定符 (或列)来定位;
  • 单元格:在HBase表中,通过行、列族 和列限定符确定一个“单元格”(cell ),单元格中存储的数据没有数据类型 ,总被视为字节数组byte[];
  • 时间戳:每个单元格都保存着同一份数 据的多个版本,这些版本采用时间戳进行索引。

HBase中需要根据行键、列族、列限定符和时间戳来确定一个单元格 。因此 ,可以视为一个“四维坐标”,即[行键, 列族, 列限定符, 时间戳]。上表中的两个元素索引如下表。

v2-336502ba4b832c9de773e7d3fdec2f77_b.jpg

对应到表中示意如下:

v2-1902c6db4fd419a849076b31702733a9_b.jpg

⭐️编写程序读取HBase数据。

如果要让Spark读取HBase,就需要使用SparkContext提供的 newAPIHadoopRDD这个API将表的内容以RDD的形式加载到Spark中。请注意代码中的注释。

#!/usr/bin/env python3
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local").setAppName("ReadHBase")#本地模式,指定AppName
sc = SparkContext(conf = conf)#生成
host = 'localhost'
table = 'student'#表名
conf = {"hbase.zookeeper.quorum": host, "hbase.mapreduce.inputtable": table}
#Hbase只能保存为字符串,因此是tostring
keyConv = "org.apache.spark.examples.pythonconverters.ImmutableBytesWritableToStringConverter"
valueConv = "org.apache.spark.examples.pythonconverters.HBaseResultToStringConverter"
hbase_rdd =sc.newAPIHadoopRDD("org.apache.hadoop.hbase.mapreduce.TableInputFormat",
                              "org.apache.hadoop.hbase.io.ImmutableBytesWritable",
                              "org.apache.hadoop.hbase.client.Result",
                              keyConverter=keyConv,
                              valueConverter=valueConv,
                              conf=conf)
count = hbase_rdd.count()
hbase_rdd.cache()
output = hbase_rdd.collect()
for (k, v) in output:
    print (k, v)

保存上述代码到文件SparkOperateHBase.py中,执行该代码文件,命令如下:

$ cd /usr/local/spark/mycode/rdd 
$ /usr/local/spark/bin/spark-submit SparkOperateHBase.py

执行结果如下:

1 {"qualifier" : "age", "timestamp" : "1545728145163", "columnFamily" :
"info", "row" : "1", "type" : "Put", "value" : "23"}
{"qualifier" : "gender", "timestamp" : "1545728114020", "columnFamily" :
"info", "row" : "1", "type" : "Put", "value" : "F"}
{"qualifier" : "name", "timestamp" : "1545728100663", "columnFamily" :
"info", "row" : "1", "type" : "Put", "value" : "Xueqian"}
2 {"qualifier" : "age", "timestamp" : "1545728184030", "columnFamily" :
"info", "row" : "2", "type" : "Put", "value" : "24"}
{"qualifier" : "gender", "timestamp" : "1545728176815", "columnFamily" :
"info", "row" : "2", "type" : "Put", "value" : "M"}
{"qualifier" : "name", "timestamp" : "1545728168727", "columnFamily" :
"info", "row" : "2", "type" : "Put", "value" : "Weiliang"}

编写程序向HBase写入数据。

下面编写应用程序把表中的两个学生信息插入到HBase的student表中。

v2-7f81362b9e1b9dab02b05b4bb8fd5c4d_b.jpg

首先编写文件SparkWriteHBase.py,输入代码如下:

#!/usr/bin/env python3
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local").setAppName("ReadHBase")
sc = SparkContext(conf = conf)
host = 'localhost'
table = 'student'
keyConv ="org.apache.spark.examples.pythonconverters.StringToImmutableBytesWritableConverter"
valueConv = "org.apache.spark.examples.pythonconverters.StringListToPutConverter"
conf = {"hbase.zookeeper.quorum": host,"hbase.mapred.outputtable":table,
        "mapreduce.outputformat.class":"org.apache.hadoop.hbase.mapreduce.TableOutputFormat",
        "mapreduce.job.output.key.class":"org.apache.hadoop.hbase.io.ImmutableBytesWritable",
        "mapreduce.job.output.value.class":"org.apache.hadoop.io.Writable"}
rawData =['3,info,name,Rongcheng','3,info,gender,M','3,info,age,26',
          '4,info,name,Guanhua','4,info,gender,M','4,info,age,27']
sc.parallelize(rawData).map(lambda x:(x[0],x.split(','))).saveAsNewAPIHadoopDataset(
        conf=conf,keyConverter=keyConv,valueConverter=valueConv)

接下来执行上述文件。

$ cd /usr/local/spark/mycode/rdd
$ /usr/local/spark/bin/spark-submit SparkWriteHBase.py

Part3.综合案例

⭐️案例1:求TOP值

四个键分别为orderid,userid,payment,productid。数据如下所示。求Top N个payment值。

v2-3a2192d470e9497dd1d9107dd7f9302e_b.jpg

首先总览一下答案代码,然后分条看一下运行逻辑。

#!/usr/bin/env python3
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local").setAppName("ReadHBase")
sc = SparkContext(conf = conf)
lines = sc.textFile("file:///usr/local/spark/mycode/rdd/file")
result1 = lines.filter(lambda line:(len(line.strip()) > 0) and (len(line.split(","))== 4))
result2 = result1.map(lambda x:x.split(",")[2])
result3 = result2.map(lambda x:(int(x),""))
result4 = result3.repartition(1)
result5 = result4.sortByKey(False)
result6 = result5.map(lambda x:x[0])
result7 = result6.take(5)
for a in result7:
    print(a)

接下来分别解读上述代码。

lines = sc.textFile("file:///usr/local/spark/mycode/rdd/file")

该语句从文件中读取数据生成RDD(名称为lines),执行后的效果如下:

v2-cff6651182dfe0630ae6197bf383b530_b.jpg
result1 = lines.filter(lambda line:(len(line.strip()) > 0) and (len(line.split(","))== 4))
result2 = result1.map(lambda x:x.split(",")[2])

该语句执行效果如下:

v2-89d41e1386003fdc56c490a142fbe345_b.jpg
result3 = result2.map(lambda x:(int(x),""))
result4 = result3.repartition(1)
result5 = result4.sortByKey(False)

该语句执行效果如下:

v2-fd328d8984673242791e8ff433b4f297_b.jpg
result6 = result5.map(lambda x:x[0])
result7 = result6.take(5)

该语句执行效果如下:

v2-3cfce5c3cedf6feddd1fc4627d4ee189_b.jpg

⭐️案例2:文件排序

有多个输入文件,每个文件中的每一行内容 均为一个整数。要求读取所有文件中的整数, 进行排序后,输出到一个新的文件中,输出 的内容个数为每行两个整数,第一个整数为 第二个整数的排序位次,第二个整数为原待 排序的整数

v2-495db4a1feeb80199ee0e9f2cb63cb05_b.jpg

v2-8566d2eefba347bdc44b94ba2d62dc82_b.jpg

首先看代码总览,然后分段观察其运行逻辑。

#!/usr/bin/env python3
from pyspark import SparkConf, SparkContext
index = 0
def getindex():
    global index
    index+=1
    return index
def main():
    conf = SparkConf().setMaster("local[1]").setAppName("FileSort")
    sc = SparkContext(conf = conf)
    lines = sc.textFile("file:///usr/local/spark/mycode/rdd/filesort/file*.txt")
    index = 0
    result1 = lines.filter(lambda line:(len(line.strip()) > 0))
    result2 = result1.map(lambda x:(int(x.strip()),""))
    result3 = result2.repartition(1)
    result4 = result3.sortByKey(True)
    result5 = result4.map(lambda x:x[0])
    result6 = result5.map(lambda x:(getindex(),x))
    result6.foreach(print)
    result6.saveAsTextFile("file:///usr/local/spark/mycode/rdd/filesort/sortresult")
if __name__ == '__main__':
    main()

接下来分段看一下结果。

lines = sc.textFile("file:///usr/local/spark/mycode/rdd/filesort/file*.txt")
result1 = lines.filter(lambda line:(len(line.strip()) > 0))

该语句执行效果如下:

v2-74b31683216bc1878e86b60b87e2c7ec_b.jpg
result2 = result1.map(lambda x:(int(x.strip()),""))
result3 = result2.repartition(1)
result4 = result3.sortByKey(True)

该语句执行效果如下:

v2-f8239b67a3e4d2dd62553ad0624a8612_b.jpg
result5 = result4.map(lambda x:x[0])
result6 = result5.map(lambda x:(getindex(),x))

该语句执行效果如下:

v2-ffc2965b5f035b8daf2959bf5099ddff_b.jpg
result6.saveAsTextFile("file:///usr/local/spark/mycode/rdd/filesort/sortresult")

该语句执行效果如下:

v2-7e944442fbded4877f00aab88de47793_b.jpg

⭐️案例3:二次排序

对于一个给定的文件(数据如file1.txt所示),请对数据进行排序,首先根据第 1列数据降序排序,如果第1列数据相等,则根据第2列数据降序排序。

v2-88f2f5c5582469d9b4d32ab19f2ed78e_b.jpg

也就是说,期望能实现如下结果:

v2-daceac25af6b1a489e2b5793491b2e87_b.jpg

这就会遇到一个问题:用于排序的key必须是可比较的对象。如下的形式是不能用于比较的:

v2-0feb153e9ba176038c26697a4642a22d_b.png

林子雨老师给出的解题思路如下:

v2-36d30ba0128850cb6741bcf558ec73e9_b.jpg

转换为文字语言就是:

  • 第一步:定义一个类SecondarySortKey,实现自定义的用于排序的key
  • 第二步:将要进行二次排序的文件加载进来生成(key,value)类型的RDD
  • 第三步:使用sortByKey()基于自定义的key进行二次排序
  • 第四步:去除掉排序的key,只保留排序的结果

两部分代码如下所示。

from operator import gt
from pyspark import SparkContext, SparkConf
class SecondarySortKey():
    def __init__(self, k):
        self.column1 = k[0]
        self.column2 = k[1]
    def __gt__(self, other):
        if other.column1 == self.column1:
            return gt(self.column2,other.column2)
        else:
            return gt(self.column1, other.column1)

def main():
    conf = SparkConf().setAppName('spark_sort').setMaster('local[1]')
    sc = SparkContext(conf=conf)
    file="file:///usr/local/spark/mycode/rdd/secondarysort/file4.txt"
    rdd1 = sc.textFile(file)
    rdd2 = rdd1.filter(lambda x:(len(x.strip()) > 0))
    rdd3 = rdd2.map(lambda x:((int(x.split(" ")[0]),int(x.split(" ")[1])),x))
    rdd4 = rdd3.map(lambda x: (SecondarySortKey(x[0]),x[1]))
    rdd5 = rdd4.sortByKey(False)
    rdd6 = rdd5.map(lambda x:x[1])
    rdd6.foreach(print)
if __name__ == '__main__':
    main()

下面分别来看一下每部分的运行逻辑。

rdd1 = sc.textFile(file)
rdd2 = rdd1.filter(lambda x:(len(x.strip()) > 0))

运行逻辑如下。

v2-f81d45c9704b5c448712922ee61a5613_b.jpg
rdd3 = rdd2.map(lambda x:((int(x.split(" ")[0]),int(x.split(" ")[1])),x))
rdd4 = rdd3.map(lambda x: (SecondarySortKey(x[0]),x[1]))

运行逻辑如下。

v2-2fcf473cec4772298e85970fa0848205_b.jpg
rdd5 = rdd4.sortByKey(False)
rdd6 = rdd5.map(lambda x:x[1])

运行逻辑如下。

v2-c2602ef9166cb6b3bf3b91f3d3b04ce8_b.jpg

总结

本文主要是对数据读写的梳理。包括从本地读写和从HBase中读写。最后通过三个例子对RDD编程系列进行一个复盘。感谢阅读 。

推荐厦门大学林子雨老师的课程:

Spark编程基础(Python版) - 网易云课堂​study.163.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值