版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
大數(shù)據(jù)基礎(chǔ):大數(shù)據(jù)概述:ApacheSpark基礎(chǔ)1大數(shù)據(jù)基礎(chǔ)概念1.1大數(shù)據(jù)的定義與特征大數(shù)據(jù)是指無法在合理時(shí)間內(nèi)用傳統(tǒng)數(shù)據(jù)處理工具進(jìn)行捕捉、管理和處理的數(shù)據(jù)集合。其特征通常被概括為“4V”:Volume(大量):數(shù)據(jù)量巨大,可能達(dá)到PB甚至EB級(jí)別。Velocity(高速):數(shù)據(jù)生成和處理速度非???,可能需要實(shí)時(shí)處理。Variety(多樣):數(shù)據(jù)類型多樣,包括結(jié)構(gòu)化、半結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)。Veracity(真實(shí)性):數(shù)據(jù)的質(zhì)量和準(zhǔn)確性,處理過程中需要考慮數(shù)據(jù)的可信度。1.1.1示例:大數(shù)據(jù)的Volume特征假設(shè)我們有一個(gè)日志文件,每天生成的數(shù)據(jù)量為1TB。使用傳統(tǒng)的關(guān)系型數(shù)據(jù)庫處理這樣的數(shù)據(jù)量將非常低效,因?yàn)殛P(guān)系型數(shù)據(jù)庫在處理大量數(shù)據(jù)時(shí),其查詢和寫入速度會(huì)顯著下降。相反,大數(shù)據(jù)處理框架如ApacheHadoop和ApacheSpark設(shè)計(jì)用于分布式處理,可以高效地處理這種規(guī)模的數(shù)據(jù)。#假設(shè)使用ApacheSpark處理1TB的日志數(shù)據(jù)
frompysparkimportSparkConf,SparkContext
conf=SparkConf().setAppName("BigDataVolumeExample").setMaster("local")
sc=SparkContext(conf=conf)
#讀取1TB的日志數(shù)據(jù)
log_data=sc.textFile("hdfs://localhost:9000/user/logs/1TB_logs.txt")
#數(shù)據(jù)處理,例如計(jì)算日志中特定關(guān)鍵詞的出現(xiàn)次數(shù)
keyword_count=log_data.filter(lambdaline:"keyword"inline).count()
print(f"關(guān)鍵詞出現(xiàn)次數(shù):{keyword_count}")1.2大數(shù)據(jù)處理的挑戰(zhàn)大數(shù)據(jù)處理面臨的主要挑戰(zhàn)包括:數(shù)據(jù)存儲(chǔ):如何有效地存儲(chǔ)PB級(jí)別的數(shù)據(jù)。數(shù)據(jù)處理速度:如何在短時(shí)間內(nèi)處理大量數(shù)據(jù)。數(shù)據(jù)多樣性:如何處理結(jié)構(gòu)化、半結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)。數(shù)據(jù)質(zhì)量:如何確保數(shù)據(jù)的準(zhǔn)確性和一致性。數(shù)據(jù)安全:如何保護(hù)數(shù)據(jù)免受未授權(quán)訪問和數(shù)據(jù)泄露。1.2.1示例:數(shù)據(jù)多樣性處理在大數(shù)據(jù)環(huán)境中,數(shù)據(jù)可能來自各種不同的源,包括社交媒體、傳感器數(shù)據(jù)、電子郵件、視頻、音頻、日志文件等。這些數(shù)據(jù)可能需要在處理前進(jìn)行清洗和轉(zhuǎn)換,以適應(yīng)分析需求。#使用ApacheSpark處理不同類型的日志數(shù)據(jù)
frompyspark.sqlimportSparkSession
spark=SparkSession.builder.appName("BigDataVarietyExample").getOrCreate()
#讀取結(jié)構(gòu)化數(shù)據(jù)
structured_data=spark.read.format("csv").option("header","true").load("hdfs://localhost:9000/user/structured_data.csv")
#讀取非結(jié)構(gòu)化數(shù)據(jù)
unstructured_data=spark.read.text("hdfs://localhost:9000/user/unstructured_data.txt")
#數(shù)據(jù)清洗和轉(zhuǎn)換
#假設(shè)我們從非結(jié)構(gòu)化數(shù)據(jù)中提取日期
unstructured_data=unstructured_data.withColumn("date",F.regexp_extract("value",r"(\d{4}-\d{2}-\d{2})",1))
#合并數(shù)據(jù)
combined_data=structured_data.union(unstructured_data.select(structured_data.columns))
#數(shù)據(jù)分析
result=combined_data.groupBy("date").count()
result.show()1.2.2數(shù)據(jù)安全大數(shù)據(jù)處理中,數(shù)據(jù)安全是一個(gè)關(guān)鍵問題。數(shù)據(jù)可能包含敏感信息,如個(gè)人身份信息、財(cái)務(wù)數(shù)據(jù)等。因此,需要實(shí)施嚴(yán)格的數(shù)據(jù)訪問控制和加密措施,以防止數(shù)據(jù)泄露。1.2.3數(shù)據(jù)質(zhì)量數(shù)據(jù)質(zhì)量直接影響到數(shù)據(jù)分析的準(zhǔn)確性和可靠性。在大數(shù)據(jù)處理中,需要實(shí)施數(shù)據(jù)清洗和驗(yàn)證流程,以確保數(shù)據(jù)的準(zhǔn)確性和一致性。1.2.4數(shù)據(jù)存儲(chǔ)由于大數(shù)據(jù)的Volume特征,傳統(tǒng)的存儲(chǔ)解決方案可能無法滿足需求。分布式文件系統(tǒng)如Hadoop的HDFS和NoSQL數(shù)據(jù)庫如ApacheCassandra被廣泛用于存儲(chǔ)大數(shù)據(jù)。通過理解大數(shù)據(jù)的定義、特征以及處理挑戰(zhàn),我們可以更好地設(shè)計(jì)和實(shí)施大數(shù)據(jù)處理系統(tǒng),以滿足現(xiàn)代數(shù)據(jù)密集型應(yīng)用的需求。2ApacheSpark入門2.1Spark的架構(gòu)與組件ApacheSpark是一個(gè)開源的分布式計(jì)算系統(tǒng),旨在提供快速、通用的數(shù)據(jù)處理能力。它支持多種計(jì)算模式,包括批處理、流處理、機(jī)器學(xué)習(xí)和圖形處理,這使得Spark成為大數(shù)據(jù)處理領(lǐng)域的強(qiáng)大工具。2.1.1架構(gòu)概述Spark的架構(gòu)主要由以下幾個(gè)關(guān)鍵組件構(gòu)成:DriverProgram:驅(qū)動(dòng)程序是Spark應(yīng)用程序的控制中心,負(fù)責(zé)調(diào)度任務(wù)、管理資源和監(jiān)控執(zhí)行狀態(tài)。ClusterManager:集群管理器負(fù)責(zé)在集群中分配資源,可以是Spark自帶的Standalone模式,也可以是YARN或Mesos等外部資源管理器。Executor:執(zhí)行器是Spark在工作節(jié)點(diǎn)上運(yùn)行的進(jìn)程,負(fù)責(zé)執(zhí)行任務(wù)并存儲(chǔ)計(jì)算結(jié)果。RDD(ResilientDistributedDataset):彈性分布式數(shù)據(jù)集是Spark的基本數(shù)據(jù)結(jié)構(gòu),是一個(gè)只讀的、可分區(qū)的分布式數(shù)據(jù)集合。SparkSQL:用于處理結(jié)構(gòu)化數(shù)據(jù),提供DataFrame和DatasetAPI,可以使用SQL查詢數(shù)據(jù)。SparkStreaming:用于處理實(shí)時(shí)數(shù)據(jù)流,可以將流數(shù)據(jù)切分為小批量進(jìn)行處理。MLlib:機(jī)器學(xué)習(xí)庫,提供多種機(jī)器學(xué)習(xí)算法和工具。GraphX:用于圖形并行計(jì)算的庫。2.1.2組件詳解DriverProgram:這是Spark應(yīng)用程序的主進(jìn)程,負(fù)責(zé)將用戶程序轉(zhuǎn)化為任務(wù),并將任務(wù)分發(fā)給執(zhí)行器。它還負(fù)責(zé)跟蹤執(zhí)行器的狀態(tài)和進(jìn)度,以及在執(zhí)行器失敗時(shí)重新調(diào)度任務(wù)。ClusterManager:集群管理器負(fù)責(zé)在集群中分配資源,它可以根據(jù)應(yīng)用程序的需求動(dòng)態(tài)分配和回收資源。Spark支持多種集群管理器,包括Standalone、YARN和Mesos,這為用戶提供了靈活的選擇。Executor:執(zhí)行器是Spark在每個(gè)工作節(jié)點(diǎn)上運(yùn)行的進(jìn)程,負(fù)責(zé)執(zhí)行任務(wù)并存儲(chǔ)計(jì)算結(jié)果。每個(gè)執(zhí)行器都有自己的JVM,可以并行處理多個(gè)任務(wù),這大大提高了計(jì)算效率。RDD:彈性分布式數(shù)據(jù)集是Spark的核心數(shù)據(jù)結(jié)構(gòu),它是一個(gè)不可變的、可分區(qū)的、容錯(cuò)的集合。RDD支持兩種操作:轉(zhuǎn)換(Transformation)和行動(dòng)(Action)。轉(zhuǎn)換操作會(huì)創(chuàng)建一個(gè)新的RDD,而行動(dòng)操作會(huì)觸發(fā)計(jì)算并返回結(jié)果。SparkSQL:SparkSQL是Spark處理結(jié)構(gòu)化數(shù)據(jù)的模塊,它提供了DataFrame和DatasetAPI,可以使用SQL查詢數(shù)據(jù),同時(shí)也支持Java、Scala、Python和R等多種語言。SparkStreaming:SparkStreaming是Spark處理實(shí)時(shí)數(shù)據(jù)流的模塊,它將流數(shù)據(jù)切分為小批量進(jìn)行處理,可以處理各種來源的數(shù)據(jù)流,包括Kafka、Flume、Twitter等。MLlib:MLlib是Spark的機(jī)器學(xué)習(xí)庫,提供了多種機(jī)器學(xué)習(xí)算法和工具,包括分類、回歸、聚類、協(xié)同過濾、降維等。GraphX:GraphX是Spark的圖形并行計(jì)算庫,它提供了圖形抽象和圖形并行操作,可以高效地處理大規(guī)模圖形數(shù)據(jù)。2.2Spark的安裝與配置2.2.1安裝步驟下載Spark:從ApacheSpark的官方網(wǎng)站下載最新版本的Spark,選擇適合你的操作系統(tǒng)的版本。解壓Spark:將下載的Spark壓縮包解壓到你選擇的目錄下。配置環(huán)境變量:將Spark的bin目錄添加到系統(tǒng)的PATH環(huán)境變量中,以便在任何目錄下都可以運(yùn)行Spark的命令。配置Hadoop:Spark依賴Hadoop的文件系統(tǒng)和資源管理器,因此需要配置Hadoop的環(huán)境。將Hadoop的配置文件(如core-site.xml和hdfs-site.xml)復(fù)制到Spark的conf目錄下。啟動(dòng)Spark:在Spark的bin目錄下,運(yùn)行sbin/start-all.sh腳本(在Linux或Mac系統(tǒng)上)或sbin/start-all.cmd腳本(在Windows系統(tǒng)上)來啟動(dòng)Spark。2.2.2配置示例假設(shè)你已經(jīng)下載并解壓了Spark,現(xiàn)在需要配置環(huán)境變量和Hadoop環(huán)境。2.2.2.1環(huán)境變量配置在Linux或Mac系統(tǒng)上,編輯/etc/environment文件,添加以下內(nèi)容:PATH=$PATH:/path/to/spark/bin在Windows系統(tǒng)上,打開系統(tǒng)環(huán)境變量編輯器,添加%SPARK_HOME%\bin到PATH環(huán)境變量中。2.2.2.2Hadoop配置將Hadoop的配置文件復(fù)制到Spark的conf目錄下。例如,如果你的Hadoop配置文件位于/path/to/hadoop/etc/hadoop目錄下,可以使用以下命令:cp/path/to/hadoop/etc/hadoop/core-site.xml/path/to/spark/conf/
cp/path/to/hadoop/etc/hadoop/hdfs-site.xml/path/to/spark/conf/2.2.2.3啟動(dòng)Spark在Spark的bin目錄下,運(yùn)行以下命令來啟動(dòng)Spark:./sbin/start-all.sh或者在Windows系統(tǒng)上運(yùn)行:sbin\start-all.cmd2.2.3運(yùn)行示例假設(shè)你已經(jīng)成功安裝并配置了Spark,現(xiàn)在可以運(yùn)行一個(gè)簡單的WordCount示例來測(cè)試你的Spark環(huán)境。2.2.3.1代碼示例#導(dǎo)入SparkContext模塊
frompysparkimportSparkContext
#創(chuàng)建SparkContext對(duì)象
sc=SparkContext("local","WordCountApp")
#讀取文本文件
text_file=sc.textFile("/path/to/your/textfile.txt")
#對(duì)文件中的每一行進(jìn)行分詞,然后對(duì)每個(gè)詞進(jìn)行計(jì)數(shù)
counts=text_file.flatMap(lambdaline:line.split(''))\
.map(lambdaword:(word,1))\
.reduceByKey(lambdaa,b:a+b)
#輸出結(jié)果
counts.saveAsTextFile("/path/to/save/your/result")2.2.3.2數(shù)據(jù)樣例假設(shè)你的文本文件textfile.txt的內(nèi)容如下:Helloworld
HelloSpark2.2.3.3代碼解釋sc.textFile("/path/to/your/textfile.txt"):這行代碼讀取位于/path/to/your/textfile.txt的文本文件,并將其轉(zhuǎn)化為一個(gè)RDD。flatMap(lambdaline:line.split('')):這行代碼將每一行文本分詞,然后將這些詞扁平化為一個(gè)RDD。map(lambdaword:(word,1)):這行代碼將每個(gè)詞轉(zhuǎn)化為一個(gè)鍵值對(duì),其中鍵是詞,值是1。reduceByKey(lambdaa,b:a+b):這行代碼將所有鍵相同的鍵值對(duì)進(jìn)行合并,合并的方式是將值相加,從而得到每個(gè)詞的出現(xiàn)次數(shù)。counts.saveAsTextFile("/path/to/save/your/result"):這行代碼將結(jié)果保存到/path/to/save/your/result目錄下。通過運(yùn)行這個(gè)WordCount示例,你可以驗(yàn)證你的Spark環(huán)境是否已經(jīng)正確安裝和配置。如果一切正常,你將在結(jié)果目錄下看到每個(gè)詞的出現(xiàn)次數(shù)。3Spark核心API:RDD的理解與操作3.1什么是RDDRDD(ResilientDistributedDataset)是ApacheSpark的核心數(shù)據(jù)結(jié)構(gòu),它是一個(gè)不可變的、分布式的數(shù)據(jù)集合。RDD提供了豐富的操作API,包括轉(zhuǎn)換(Transformation)和行動(dòng)(Action)兩種類型,使得數(shù)據(jù)處理既高效又靈活。RDD具有容錯(cuò)性,能夠自動(dòng)恢復(fù)數(shù)據(jù)丟失,同時(shí)它支持懶加載,即在需要時(shí)才執(zhí)行計(jì)算,這大大提高了數(shù)據(jù)處理的效率。3.1.1RDD的特性不可變性:一旦創(chuàng)建,RDD的數(shù)據(jù)不能被修改,只能通過轉(zhuǎn)換操作創(chuàng)建新的RDD。分區(qū):RDD的數(shù)據(jù)被劃分為多個(gè)分區(qū),每個(gè)分區(qū)可以獨(dú)立計(jì)算,這使得并行處理成為可能。容錯(cuò)性:RDD能夠自動(dòng)檢測(cè)數(shù)據(jù)丟失,并從其他節(jié)點(diǎn)恢復(fù)數(shù)據(jù)。血統(tǒng):每個(gè)RDD都有一個(gè)血統(tǒng)圖,記錄了其數(shù)據(jù)的來源和轉(zhuǎn)換過程,這有助于Spark在數(shù)據(jù)丟失時(shí)進(jìn)行恢復(fù)。3.1.2RDD的操作類型轉(zhuǎn)換(Transformation):創(chuàng)建新的RDD,如map,filter,reduceByKey等。行動(dòng)(Action):觸發(fā)RDD的計(jì)算,如count,collect,saveAsTextFile等。3.2示例:使用RDD進(jìn)行數(shù)據(jù)處理假設(shè)我們有一個(gè)文本文件data.txt,其中包含以下內(nèi)容:1,John,Doe
2,Jane,Smith
3,Michael,Johnson我們將使用Spark的RDD來讀取這個(gè)文件,然后進(jìn)行一些基本的數(shù)據(jù)處理操作。#導(dǎo)入Spark相關(guān)庫
frompysparkimportSparkConf,SparkContext
#初始化SparkContext
conf=SparkConf().setAppName("RDDExample").setMaster("local")
sc=SparkContext(conf=conf)
#讀取文本文件
lines=sc.textFile("data.txt")
#使用map轉(zhuǎn)換,將每行數(shù)據(jù)轉(zhuǎn)換為元組
data=lines.map(lambdaline:line.split(','))
#使用filter行動(dòng),篩選出姓氏為Smith的記錄
smiths=data.filter(lambdax:x[2]=="Smith")
#使用collect行動(dòng),收集所有篩選出的數(shù)據(jù)
result=smiths.collect()
#輸出結(jié)果
forrecordinresult:
print(record)3.2.1代碼解釋初始化SparkContext:我們首先創(chuàng)建一個(gè)SparkConf對(duì)象來配置Spark應(yīng)用,然后使用這個(gè)配置創(chuàng)建一個(gè)SparkContext對(duì)象,這是使用Spark進(jìn)行數(shù)據(jù)處理的起點(diǎn)。讀取數(shù)據(jù):使用textFile方法讀取data.txt文件,返回一個(gè)RDD,其中每個(gè)元素是一行文本。轉(zhuǎn)換數(shù)據(jù):使用map操作,將每一行文本數(shù)據(jù)轉(zhuǎn)換為一個(gè)元組,元組中的每個(gè)元素對(duì)應(yīng)文本中的一列數(shù)據(jù)。篩選數(shù)據(jù):使用filter操作,篩選出所有姓氏為Smith的記錄。收集數(shù)據(jù):使用collect行動(dòng),將篩選出的數(shù)據(jù)收集到驅(qū)動(dòng)程序中,然后輸出。3.3Spark核心API:DataFrame與DataSet的使用3.3.1DataFrame簡介DataFrame是SparkSQL中的數(shù)據(jù)結(jié)構(gòu),它是一個(gè)分布式的行集合,每行有固定數(shù)量的列。DataFrame可以被視為一個(gè)增強(qiáng)版的RDD,它提供了更豐富的API,支持SQL查詢,并且具有類型安全和列名的元數(shù)據(jù)信息。3.3.2DataSet簡介DataSet是Spark2.0引入的數(shù)據(jù)結(jié)構(gòu),它結(jié)合了RDD的靈活性和DataFrame的類型安全,是一個(gè)分布式的、類型安全的、可序列化的數(shù)據(jù)集合。DataSet可以使用Java、Scala或Python編寫,但在Python中,它通常被視為DataFrame的同義詞,因?yàn)樗鼈兪褂孟嗤腁PI。3.3.3DataFrame與DataSet的使用3.3.3.1創(chuàng)建DataFramefrompyspark.sqlimportSparkSession
frompyspark.sql.typesimportStructType,StructField,IntegerType,StringType
#初始化SparkSession
spark=SparkSession.builder.appName("DataFrameExample").getOrCreate()
#定義Schema
schema=StructType([
StructField("id",IntegerType(),True),
StructField("first_name",StringType(),True),
StructField("last_name",StringType(),True)
])
#創(chuàng)建DataFrame
df=spark.read.format("csv").option("header","true").schema(schema).load("data.txt")
#顯示DataFrame的前幾行
df.show()3.3.3.2DataFrame操作#篩選姓氏為Smith的記錄
smiths_df=df.filter(df.last_name=="Smith")
#使用SQL查詢
df.createOrReplaceTempView("people")
result=spark.sql("SELECT*FROMpeopleWHERElast_name='Smith'")
result.show()3.3.4代碼解釋初始化SparkSession:SparkSession是使用SparkSQL的入口點(diǎn),它提供了創(chuàng)建DataFrame和執(zhí)行SQL查詢的能力。定義Schema:在讀取CSV文件時(shí),我們定義了一個(gè)StructType來描述數(shù)據(jù)的結(jié)構(gòu),包括每列的名稱和類型。創(chuàng)建DataFrame:使用spark.read方法讀取CSV文件,并使用定義的Schema創(chuàng)建DataFrame。篩選數(shù)據(jù):使用filter操作,基于DataFrame的列名和類型安全的條件篩選數(shù)據(jù)。SQL查詢:將DataFrame注冊(cè)為臨時(shí)視圖,然后使用spark.sql執(zhí)行SQL查詢,這展示了DataFrame與SQL查詢的無縫集成。3.4結(jié)論通過上述示例,我們了解了Spark中RDD和DataFrame的基本使用,包括如何讀取數(shù)據(jù)、轉(zhuǎn)換數(shù)據(jù)和篩選數(shù)據(jù)。RDD提供了基礎(chǔ)的數(shù)據(jù)處理能力,而DataFrame和DataSet則提供了更高級(jí)、更類型安全的數(shù)據(jù)處理API,使得大數(shù)據(jù)處理既高效又易于管理。4SparkSQL詳解4.1SparkSQL的基本操作SparkSQL是ApacheSpark的一個(gè)模塊,用于處理結(jié)構(gòu)化和半結(jié)構(gòu)化數(shù)據(jù)。它提供了編程接口,允許用戶使用SQL語句或DataFrameAPI進(jìn)行數(shù)據(jù)查詢和操作。下面,我們將通過一個(gè)具體的例子來了解如何使用SparkSQL進(jìn)行基本操作。4.1.1環(huán)境準(zhǔn)備首先,確保你已經(jīng)安裝了ApacheSpark和相關(guān)的Python庫pyspark。在本例中,我們將使用Python作為編程語言。4.1.2創(chuàng)建SparkSessionfrompyspark.sqlimportSparkSession
#創(chuàng)建SparkSession
spark=SparkSession.builder\
.appName("SparkSQLExample")\
.getOrCreate()4.1.3加載數(shù)據(jù)假設(shè)我們有一個(gè)CSV文件,包含以下數(shù)據(jù):name,age,city
Alice,30,NewYork
Bob,25,LosAngeles
Charlie,35,Chicago我們可以使用read方法加載這個(gè)CSV文件到DataFrame中:#加載CSV文件
df=spark.read.format("csv")\
.option("header","true")\
.option("inferSchema","true")\
.load("path/to/your/csvfile.csv")
#顯示DataFrame的前幾行
df.show()4.1.4注冊(cè)臨時(shí)表將DataFrame注冊(cè)為臨時(shí)表,以便使用SQL語句進(jìn)行查詢:df.createOrReplaceTempView("people")4.1.5執(zhí)行SQL查詢使用sql方法執(zhí)行SQL查詢,并將結(jié)果存儲(chǔ)在新的DataFrame中:#查詢年齡大于30的人
result_df=spark.sql("SELECT*FROMpeopleWHEREage>30")
#顯示查詢結(jié)果
result_df.show()4.1.6數(shù)據(jù)操作除了SQL查詢,我們還可以使用DataFrameAPI進(jìn)行數(shù)據(jù)操作,例如選擇特定列、過濾數(shù)據(jù)、排序等:#選擇特定列
selected_df=df.select("name","city")
#過濾數(shù)據(jù)
filtered_df=df.filter(df.age>30)
#排序數(shù)據(jù)
sorted_df=df.orderBy(df.age.desc())
#顯示操作結(jié)果
selected_df.show()
filtered_df.show()
sorted_df.show()4.2連接外部數(shù)據(jù)庫SparkSQL還支持直接從外部數(shù)據(jù)庫讀取數(shù)據(jù),例如MySQL、PostgreSQL等。下面是一個(gè)使用JDBC連接MySQL數(shù)據(jù)庫的例子。4.2.1配置JDBC驅(qū)動(dòng)確保你已經(jīng)下載了相應(yīng)的JDBC驅(qū)動(dòng),并將其添加到Spark的JAR包中。4.2.2連接數(shù)據(jù)庫使用read方法和jdbc格式連接數(shù)據(jù)庫:#連接MySQL數(shù)據(jù)庫
jdbc_df=spark.read.format("jdbc")\
.option("url","jdbc:mysql://localhost:3306/yourdatabase")\
.option("driver","com.mysql.jdbc.Driver")\
.option("dbtable","yourtable")\
.option("user","yourusername")\
.option("password","yourpassword")\
.load()
#顯示從數(shù)據(jù)庫讀取的數(shù)據(jù)
jdbc_df.show()4.2.3寫入數(shù)據(jù)庫同樣,我們也可以使用write方法將DataFrame中的數(shù)據(jù)寫回到數(shù)據(jù)庫中:#將DataFrame寫入數(shù)據(jù)庫
jdbc_df.write.format("jdbc")\
.option("url","jdbc:mysql://localhost:3306/yourdatabase")\
.option("driver","com.mysql.jdbc.Driver")\
.option("dbtable","yourtable")\
.option("user","yourusername")\
.option("password","yourpassword")\
.mode("append")\
.save()通過以上步驟,我們不僅了解了如何使用SparkSQL進(jìn)行基本的數(shù)據(jù)操作,還學(xué)會(huì)了如何與外部數(shù)據(jù)庫進(jìn)行交互,這對(duì)于處理大規(guī)模數(shù)據(jù)集時(shí)非常有用。5Spark流處理5.1SparkStreaming簡介SparkStreaming是ApacheSpark的一個(gè)重要模塊,用于處理實(shí)時(shí)數(shù)據(jù)流。它將實(shí)時(shí)數(shù)據(jù)流切分為一系列小的批處理數(shù)據(jù),然后使用SparkCore的API進(jìn)行處理,從而實(shí)現(xiàn)流式數(shù)據(jù)的實(shí)時(shí)處理。SparkStreaming可以接收來自Kafka、Flume、Twitter、ZeroMQ、TCP套接字等數(shù)據(jù)源的實(shí)時(shí)數(shù)據(jù)流,并且可以將處理后的結(jié)果實(shí)時(shí)地推送到文件系統(tǒng)、數(shù)據(jù)庫和實(shí)時(shí)儀表板中。5.1.1特點(diǎn)微批處理架構(gòu):SparkStreaming將流式數(shù)據(jù)處理為一系列微批處理,每個(gè)批處理可以獨(dú)立處理,這使得SparkStreaming能夠處理大規(guī)模的實(shí)時(shí)數(shù)據(jù)流。容錯(cuò)性:SparkStreaming利用Spark的容錯(cuò)機(jī)制,能夠自動(dòng)恢復(fù)數(shù)據(jù)流處理中的故障,保證數(shù)據(jù)處理的正確性和完整性。集成性:SparkStreaming與Spark的其他模塊(如SparkSQL、MLlib和GraphX)無縫集成,使得在實(shí)時(shí)數(shù)據(jù)流中進(jìn)行復(fù)雜的數(shù)據(jù)處理和分析成為可能。5.1.2基本概念DStream:DStream是SparkStreaming中的基本數(shù)據(jù)抽象,表示一個(gè)連續(xù)的數(shù)據(jù)流。DStream可以看作是一個(gè)RDD的序列,每個(gè)RDD代表一個(gè)時(shí)間間隔內(nèi)的數(shù)據(jù)。時(shí)間間隔:SparkStreaming將時(shí)間切分為一系列的間隔,每個(gè)間隔內(nèi)的數(shù)據(jù)被處理為一個(gè)批處理。時(shí)間間隔的長度可以通過batchDuration參數(shù)進(jìn)行設(shè)置。5.2DStream操作DStream提供了豐富的操作,可以對(duì)實(shí)時(shí)數(shù)據(jù)流進(jìn)行各種處理。下面將詳細(xì)介紹一些基本的DStream操作。5.2.1轉(zhuǎn)換操作轉(zhuǎn)換操作類似于SparkRDD上的操作,用于改變DStream中的數(shù)據(jù)。以下是一些常見的轉(zhuǎn)換操作:5.2.1.1map(func)map操作將DStream中的每個(gè)元素應(yīng)用函數(shù)func,并返回一個(gè)新的DStream。#示例代碼
frompysparkimportSparkContext
frompyspark.streamingimportStreamingContext
sc=SparkContext("local[2]","DStreamMapExample")
ssc=StreamingContext(sc,1)#設(shè)置時(shí)間間隔為1秒
#創(chuàng)建一個(gè)接收TCP數(shù)據(jù)的DStream
lines=ssc.socketTextStream("localhost",9999)
#使用map操作將每行數(shù)據(jù)轉(zhuǎn)換為長度
lengths=lines.map(lambdaline:len(line))
#打印結(jié)果
lengths.pprint()
ssc.start()
ssc.awaitTermination()5.2.1.2filter(func)filter操作用于篩選DStream中的元素,只保留滿足函數(shù)func的元素。#示例代碼
#繼續(xù)使用上面的linesDStream
words=lines.flatMap(lambdaline:line.split(""))
filtered_words=words.filter(lambdaword:"spark"inword.lower())
filtered_words.pprint()5.2.1.3reduceByKey(func)reduceByKey操作用于對(duì)DStream中的鍵值對(duì)進(jìn)行聚合,使用函數(shù)func對(duì)具有相同鍵的值進(jìn)行聚合。#示例代碼
#假設(shè)每行數(shù)據(jù)是一個(gè)鍵值對(duì),例如"word:1"
pairs=lines.map(lambdaline:line.split(":"))
word_counts=pairs.reduceByKey(lambdaa,b:a+int(b))
word_counts.pprint()5.2.2輸出操作輸出操作用于將DStream中的數(shù)據(jù)輸出到外部系統(tǒng),如文件系統(tǒng)、數(shù)據(jù)庫或?qū)崟r(shí)儀表板。5.2.2.1saveAsTextFiles(prefix,[suffix])saveAsTextFiles操作將DStream中的數(shù)據(jù)保存為文本文件。#示例代碼
#將上面的word_countsDStream保存為文本文件
word_counts.saveAsTextFiles("hdfs://localhost:9000/streaming_output")5.2.2.2foreachRDD(func)foreachRDD操作允許你對(duì)DStream中的每個(gè)RDD執(zhí)行任意操作,如將數(shù)據(jù)寫入數(shù)據(jù)庫。#示例代碼
defprocess_rdd(rdd):
#假設(shè)你有一個(gè)數(shù)據(jù)庫連接
conn=get_db_connection()
forword,countinrdd.collect():
#將數(shù)據(jù)寫入數(shù)據(jù)庫
conn.execute("INSERTINTOword_counts(word,count)VALUES(?,?)",(word,count))
conn.close()
word_counts.foreachRDD(process_rdd)5.2.3狀態(tài)操作狀態(tài)操作允許DStream中的操作具有狀態(tài),即可以記住之前的數(shù)據(jù)和結(jié)果。5.2.3.1updateStateByKey(func)updateStateByKey操作用于更新每個(gè)鍵的狀態(tài),使用函數(shù)func來更新狀態(tài)。#示例代碼
frompyspark.streamingimportDStream
defupdate_func(new_values,last_sum):
returnsum(new_values)+(last_sumor0)
#假設(shè)每行數(shù)據(jù)是一個(gè)鍵值對(duì),例如"word:1"
pairs=lines.map(lambdaline:line.split(":"))
word_counts=pairs.updateStateByKey(update_func)
word_counts.pprint()通過上述示例,我們可以看到SparkStreaming如何使用DStream進(jìn)行實(shí)時(shí)數(shù)據(jù)流的處理,包括數(shù)據(jù)的轉(zhuǎn)換、篩選、聚合和輸出。這些操作使得SparkStreaming成為處理大規(guī)模實(shí)時(shí)數(shù)據(jù)流的強(qiáng)大工具。6Spark機(jī)器學(xué)習(xí)6.1MLlib庫介紹MLlib是ApacheSpark中的機(jī)器學(xué)習(xí)庫,它提供了豐富的算法,包括分類、回歸、聚類、協(xié)同過濾、降維、特征提取、選擇和轉(zhuǎn)換,以及模型評(píng)估和數(shù)據(jù)導(dǎo)入工具。MLlib的設(shè)計(jì)目標(biāo)是使機(jī)器學(xué)習(xí)的實(shí)現(xiàn)和應(yīng)用變得簡單、高效,同時(shí)能夠處理大規(guī)模數(shù)據(jù)集。它利用Spark的RDD(彈性分布式數(shù)據(jù)集)和DataFrame/DataSetAPI,為數(shù)據(jù)科學(xué)家和工程師提供了一個(gè)強(qiáng)大的分布式計(jì)算框架。6.1.1MLlib的主要特性分布式計(jì)算:MLlib利用Spark的分布式計(jì)算能力,能夠處理大規(guī)模數(shù)據(jù)集,適用于大數(shù)據(jù)分析場景。算法豐富:包括常見的監(jiān)督學(xué)習(xí)、無監(jiān)督學(xué)習(xí)算法,以及推薦系統(tǒng)、降維等高級(jí)功能。易于使用:提供了高級(jí)API,使得機(jī)器學(xué)習(xí)模型的構(gòu)建和訓(xùn)練變得簡單,同時(shí)支持多種編程語言(Scala、Java、Python)。集成性:與Spark的其他組件(如SQL、Streaming)無縫集成,便于構(gòu)建復(fù)雜的數(shù)據(jù)處理和分析流程。6.2機(jī)器學(xué)習(xí)算法應(yīng)用6.2.1分類算法:邏輯回歸邏輯回歸是一種廣泛使用的分類算法,適用于二分類和多分類問題。在SparkMLlib中,邏輯回歸模型可以使用LogisticRegression類來構(gòu)建。6.2.1.1示例代碼frompyspark.ml.classificationimportLogisticRegression
frompyspark.ml.featureimportVectorAssembler
frompyspark.sqlimportSparkSession
#初始化SparkSession
spark=SparkSession.builder.appName("LogisticRegressionExample").getOrCreate()
#加載數(shù)據(jù)
data=spark.read.format("libsvm").load("data/mllib/sample_libsvm_data.txt")
#特征組裝
assembler=VectorAssembler(inputCols=data.columns[:-1],outputCol="features")
data=assembler.transform(data)
#劃分?jǐn)?shù)據(jù)集
train_data,test_data=data.randomSplit([0.7,0.3])
#創(chuàng)建邏輯回歸模型
lr=LogisticRegression(maxIter=10,regParam=0.3,elasticNetParam=0.8)
#訓(xùn)練模型
model=lr.fit(train_data)
#預(yù)測(cè)
predictions=model.transform(test_data)
#評(píng)估模型
frompyspark.ml.evaluationimportBinaryClassificationEvaluator
evaluator=BinaryClassificationEvaluator()
accuracy=evaluator.evaluate(predictions)
print("TestError=%g"%(1.0-accuracy))
#關(guān)閉SparkSession
spark.stop()6.2.1.2數(shù)據(jù)樣例數(shù)據(jù)文件data/mllib/sample_libsvm_data.txt中的數(shù)據(jù)樣例如下:10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.0
10:1.01:1.02:1.03:1.04:1.05:1.06:1.07:1.08:1.06.2.2聚類算法:K-MeansK-Means是一種無監(jiān)督學(xué)習(xí)算法,用于數(shù)據(jù)聚類。在SparkMLlib中,K-Means算法通過KMeans類實(shí)現(xiàn)。6.2.2.1示例代碼frompyspark.ml.clusteringimportKMeans
frompyspark.ml.featureimportVectorAssembler
frompyspark.sqlimportSparkSession
#初始化SparkSession
spark=SparkSession.builder.appName("KMeansExample").getOrCreate()
#加載數(shù)據(jù)
data=spark.read.format("libsvm").load("data/mllib/sample_kmeans_data.txt")
#特征組裝
assembler=VectorAssembler(inputCols=data.columns[:-1],outputCol="features")
data=assembler.transform(data)
#創(chuàng)建K-Means模型
kmeans=KMeans(k=2,seed=1)
#訓(xùn)練模型
model=kmeans.fit(data)
#預(yù)測(cè)
predictions=model.transform(data)
#評(píng)估模型
frompyspark.ml.evaluationimportClusteringEvaluator
evaluator=ClusteringEvaluator()
silhouette=evaluator.evaluate(predictions)
print("Silhouettewithsquaredeuclideandistance="+str(silhouette))
#關(guān)閉SparkSession
spark.stop()6.2.2.2數(shù)據(jù)樣例數(shù)據(jù)文件data/mllib/sample_kmeans_data.txt中的數(shù)據(jù)樣例如下:00:1.01:0.1
10:0.11:1.0
20:0.21:0.9
30:0.31:0.8
40:0.41:0.7
50:0.51:0.6
60:0.61:0.5
70:0.71:0.4
80:0.81:0.3
90:0.91:0.26.2.3降維算法:PCA主成分分析(PCA)是一種常用的降維算法,用于減少數(shù)據(jù)的維度,同時(shí)保留數(shù)據(jù)的大部分信息。在SparkMLlib中,PCA算法通過PCA類實(shí)現(xiàn)。6.2.3.1示例代碼frompyspark.ml.linalgimportVectors
frompyspark.ml.featureimportPCA
frompyspark.sqlimportSparkSession
#初始化SparkSession
spark=SparkSession.builder.appName("PCAExample").getOrCreate()
#創(chuàng)建數(shù)據(jù)
data=[(Vectors.sparse(5,[(1,1.0),(3,7.0)]),),
(Vectors.dense([2.0,0.0,3.0,4.0,5.0]),),
(Vectors.dense([4.0,0.0,0.0,6.0,7.0]),)]
df=spark.createDataFrame(data,["features"])
#創(chuàng)建PCA模型
pca=PCA(k=3,inputCol="features",outputCol="pcaFeatures")
#訓(xùn)練模型
model=pca.fit(df)
#變換數(shù)據(jù)
result=model.transform(df).select("pcaFeatures")
#顯示結(jié)果
result.show(truncate=False)
#關(guān)閉SparkSession
spark.stop()6.2.3.2數(shù)據(jù)樣例數(shù)據(jù)樣例直接在代碼中創(chuàng)建,如下所示:data=[(Vectors.sparse(5,[(1,1.0),(3,7.0)]),),
(Vectors.dense([2.0,0.0,3.0,4.0,5.0]),),
(Vectors.dense([4.0,0.0,0.0,6.0,7.0]),)]每個(gè)元組代表一個(gè)數(shù)據(jù)點(diǎn),其中Vectors.sparse和Vectors.dense分別用于創(chuàng)建稀疏向量和密集向量,表示數(shù)據(jù)點(diǎn)的特征。通過以上示例,我們可以看到如何在Spark中使用MLlib庫來實(shí)現(xiàn)和應(yīng)用機(jī)器學(xué)習(xí)算法,包括分類、聚類和降維。這些算法的實(shí)現(xiàn)不僅高效,而且易于使用,非常適合處理大規(guī)模數(shù)據(jù)集。7Spark性能優(yōu)化7.1Spark性能調(diào)優(yōu)策略在處理大數(shù)據(jù)時(shí),ApacheSpark因其高效的數(shù)據(jù)處理能力和易于使用的API而受到廣泛歡迎。然而,隨著數(shù)據(jù)量的增加和復(fù)雜性的提高,Spark作業(yè)的性能可能會(huì)受到影響。為了確保Spark作業(yè)能夠高效運(yùn)行,以下是一些關(guān)鍵的性能調(diào)優(yōu)策略:7.1.1選擇合適的部署模式Standalone模式:適用于小型集群,易于設(shè)置和管理。YARN模式:適用于與HadoopYARN集成的環(huán)境,可以與其他YARN應(yīng)用共享資源。Mesos模式:適用于需要更細(xì)粒度資源管理和調(diào)度的環(huán)境。7.1.2調(diào)整Executor和Task的數(shù)量Executor數(shù)量:根據(jù)集群的資源和作業(yè)的性質(zhì)調(diào)整。過多的Executor會(huì)導(dǎo)致資源浪費(fèi),過少則可能限制并行度。Task數(shù)量:每個(gè)Executor上的Task數(shù)量也應(yīng)根據(jù)數(shù)據(jù)集的大小和集群的CPU核心數(shù)進(jìn)行調(diào)整。7.1.3優(yōu)化數(shù)據(jù)的Shuffle操作Shuffle操作是Spark中最耗時(shí)的部分之一。減少Shuffle操作的數(shù)量和大小可以顯著提高性能。7.1.3.1示例代碼#通過減少分區(qū)數(shù)量來優(yōu)化Shuffle操作
rdd=sc.parallelize(range(1000000),100)#原始分區(qū)數(shù)為100
rdd=rdd.repartition(10)#減少分區(qū)數(shù)到107.1.4使用Broadcast變量對(duì)于需要在多個(gè)Task中共享的大數(shù)據(jù)集,使用Broadcast變量可以減少數(shù)據(jù)在網(wǎng)絡(luò)中的傳輸,從而提高性能。7.1.4.1示例代碼#使用Broadcast變量
frompysparkimportSparkContext,SparkConf
conf=SparkConf().setAppName("BroadcastExample")
sc=SparkContext(conf=conf)
data=sc.parallelize(range(1000000))
large_data_set=range(100000000)
broadcast_data=sc.broadcast(large_data_set)
result=data.map(lambdax:broadcast_data.value[x%len(broadcast_data.value)])7.1.5選擇正確的數(shù)據(jù)結(jié)構(gòu)RDD:適合需要容錯(cuò)和復(fù)雜操作的場景。DataFrame:提供了優(yōu)化的執(zhí)行計(jì)劃,適合結(jié)構(gòu)化數(shù)據(jù)處理。Dataset:結(jié)合了RDD的強(qiáng)類型和DataFrame的優(yōu)化,是Spark2.x及以后版本的推薦選擇。7.1.6優(yōu)化數(shù)據(jù)讀取和寫入使用Parquet、ORC等列式存儲(chǔ)格式:這些格式可以提高讀取速度,因?yàn)樗鼈冊(cè)试SSpark跳過不必要的列數(shù)據(jù)。壓縮數(shù)據(jù):使用壓縮可以減少數(shù)據(jù)的傳輸和存儲(chǔ)成本。7.2內(nèi)存與存儲(chǔ)優(yōu)化Spark的性能在很大程度上依賴于內(nèi)存的使用。以下是一些關(guān)于內(nèi)存和存儲(chǔ)優(yōu)化的策略:7.2.1調(diào)整Spark的內(nèi)存配置spark.executor.memory:設(shè)置Executor的內(nèi)存大小。spark.driver.memory:設(shè)置Driver的內(nèi)存大小。spark.memory.fraction:設(shè)置用于存儲(chǔ)和執(zhí)行的內(nèi)存比例。7.2.2使用persist或cache方法persist和cache方法可以將RDD存儲(chǔ)在內(nèi)存中,避免重復(fù)計(jì)算。選擇正確的存儲(chǔ)級(jí)別(如MEMORY_ONLY、MEMORY_AND_DISK等)對(duì)于性能至關(guān)重要。7.2.2.1示例代碼#使用persist方法
rdd=sc.parallelize(range(1000000))
rdd.persist(storageLevel=StorageLevel.MEMORY_AND_DISK)7.2.3控制數(shù)據(jù)的序列化方式spark.serializer:默認(rèn)使用Java序列化,但可以更改為Kryo序列化以提高性能。7.2.4優(yōu)化磁盤I/O減少小文件的數(shù)量:小文件會(huì)增加Spark的元數(shù)據(jù)開銷,可以通過合并小文件來優(yōu)化。使用高效的文件系統(tǒng):如HDFS、S3等,這些系統(tǒng)提供了更好的I/O性能。7.2.5使用TTL緩存對(duì)于長時(shí)間運(yùn)行的作業(yè),可以使用TTL緩存來自動(dòng)清除不再需要的數(shù)據(jù),釋放內(nèi)存空間。7.2.5.1示例代碼#使用TTL緩存
rdd=sc.parallelize(range(1000000))
rdd.cache()
sc.setCheckpointDir("/path/to/checkpoint")通過上述策略,可以顯著提高ApacheSpark作業(yè)的性能,確保大數(shù)據(jù)處理任務(wù)能夠高效、快速地完成。在實(shí)際應(yīng)用中,可能需要根據(jù)具體場景和數(shù)據(jù)特性進(jìn)行調(diào)整和優(yōu)化。8實(shí)戰(zhàn)項(xiàng)目演練8.1數(shù)據(jù)預(yù)處理數(shù)據(jù)預(yù)處理是大數(shù)據(jù)分析的關(guān)鍵步驟,它包括數(shù)據(jù)清洗、數(shù)據(jù)集成、數(shù)據(jù)轉(zhuǎn)換和數(shù)據(jù)規(guī)約。在ApacheSpar
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024屆高考語文一輪復(fù)習(xí)第1章信息類文本閱讀4第三節(jié)概念理解和要點(diǎn)概括題-找準(zhǔn)命題角度定位細(xì)微差別課件
- 工程全過程審計(jì)實(shí)施要點(diǎn)及案例分析
- 2024年低壓電工試題及答案
- 古詩詞誦讀《虞美人(春花秋月何時(shí)了)》課件 2024-2025學(xué)年統(tǒng)編版高中語文必修上冊(cè)
- 甘肅省天水市蘭州市2025屆高三一診考試數(shù)學(xué)試卷含解析
- 江蘇省鎮(zhèn)江一中等2025屆高考語文押題試卷含解析
- 廣東省十校2025屆高考臨考沖刺語文試卷含解析
- 2025屆福建省上杭縣一中高考沖刺英語模擬試題含解析
- 湖南省“五市十?!?025屆高考數(shù)學(xué)五模試卷含解析
- 10.1《勸學(xué)》課件 2024-2025學(xué)年統(tǒng)編版高中語文必修上冊(cè)-2
- 語文第21課《小圣施威降大圣》課件 2024-2025學(xué)年統(tǒng)編版語文七年級(jí)上冊(cè)
- 產(chǎn)學(xué)研合作中的法律問題
- 2024年中考數(shù)學(xué)真題完全解讀(湖南卷)
- 《概率論與數(shù)理統(tǒng)計(jì)》教材
- 2024年山東文旅云智能科技限公司招聘11人高頻500題難、易錯(cuò)點(diǎn)模擬試題附帶答案詳解
- 空氣動(dòng)力學(xué)數(shù)值方法:有限差分法(FDM):有限差分法的邊界條件處理
- 常住人口登記表(集體戶口)-英文翻譯
- ??啤斗ɡ韺W(xué)》(第三版教材)形成性考核試題及答案
- 2024年秋七年級(jí)歷史上冊(cè) 第17課 西晉的短暫統(tǒng)一和北方各族的內(nèi)遷教案 新人教版
- 做成功父母 培養(yǎng)出色孩子
- 一年級(jí)小學(xué)數(shù)學(xué)下冊(cè)應(yīng)用題800道
評(píng)論
0/150
提交評(píng)論