大數(shù)據(jù)開發(fā)面試之26個Spark高頻考點

前言
        今天為大家?guī)泶髷?shù)據(jù)開發(fā)面試中,關(guān)于 Spark 的 28 個高頻考點 。另外文末為大家準(zhǔn)備了福利,不要錯過 !

1、Spark 如何保證宕機(jī)迅速恢復(fù)?
        適當(dāng)增加 spark standby master

        編寫 shell 腳本,定期檢測 master 狀態(tài),出現(xiàn)宕機(jī)后對 master 進(jìn)行重啟操作

2. Spark streaming 以及基本工作原理?
        Spark streaming 是 spark core API 的一種擴(kuò)展,可以用于進(jìn)行大規(guī)模、高吞吐量、容錯的實時數(shù)據(jù)流的處理。

        它支持從多種數(shù)據(jù)源讀取數(shù)據(jù),比如 Kafka、Flume、Twitter 和 TCP Socket,并且能夠使用算子比如 map、reduce、join 和 window 等來處理數(shù)據(jù),處理后的數(shù)據(jù)可以保存到文件系統(tǒng)、數(shù)據(jù)庫等存儲中。

        Spark streaming 內(nèi)部的基本工作原理是:接受實時輸入數(shù)據(jù)流,然后將數(shù)據(jù)拆分成 batch ,比如每收集一秒的數(shù)據(jù)封裝成一個 batch,然后將每個 batch 交給 spark 的計算引擎進(jìn)行處理,最后會生產(chǎn)出一個結(jié)果數(shù)據(jù)流,其中的數(shù)據(jù)也是一個一個的 batch 組成的 。

3. Spark 有哪些組件?
        master:管理集群和節(jié)點,不參與計算。

       worker:計算節(jié)點,進(jìn)程本身不參與計算,和 master 匯報。Driver:運行程序的 main 方法,創(chuàng)建 spark context 對象。

        spark context:控制整個 application 的生命周期,包括 dagsheduler 和 task scheduler 等組件。

        client:用戶提交程序的入口。

4、Spark 工作機(jī)制
        用戶在 client 端提交作業(yè)后,會由 Driver 運行 main 方法并創(chuàng)建 spark context 上下文。執(zhí)行 add 算子,形成 dag 圖輸入 dagscheduler,按照 add 之間的依賴關(guān)系劃分 stage 輸入 task scheduler。task scheduler 會將 stage 劃分為 task set 分發(fā)到各個節(jié)點的 executor 中執(zhí)行。

5、Spark 主備切換機(jī)制原理知道嗎?
        Master 實際上可以配置兩個,Spark 原生的 standalone 模式是支持 Master 主備切換的。當(dāng) Active Master 節(jié)點掛掉以后,可以將 Standby Master 切換為 Active Master。

        Spark Master 主備切換可以基于兩種機(jī)制,一種是基于文件系統(tǒng)的,一種是基于 ZooKeeper 的。

        基于文件系統(tǒng)的主備切換機(jī)制,需要在 Active Master 掛掉之后手動切換到 Standby Master 上;而基于 Zookeeper 的主備切換機(jī)制,可以實現(xiàn)自動切換 Master 。

6. Spark 的有幾種部署模式,每種模式特點?
1)本地模式       

        Spark 不一定非要跑在 hadoop 集群,可以在本地,起多個線程的方式來指定。將 Spark 應(yīng)用以多線程的方式直接運行在本地,一般都是為了方便調(diào)試 。

        本地模式分三類

local     :只啟動一個 executor
local[k] :啟動 k 個 executor
local[*] :啟動跟 cpu 數(shù)目相同的 executor
2)standalone 模式

        分布式部署集群,自帶完整的服務(wù),資源管理和任務(wù)監(jiān)控是 Spark 自己監(jiān)控,這個模式也是其他模式的基礎(chǔ) 。

3)Spark on yarn 模式

        分布式部署集群,資源和任務(wù)監(jiān)控交給 yarn 管理,但是目前僅支持粗粒度資源分配方式,包含 cluster 和 client 運行模式,cluster 適合生產(chǎn),driver 運行在集群子節(jié)點,具有容錯功能,client 適合調(diào)試,dirver 運行在客戶端。

4)Spark On Mesos 模式

        官方推薦這種模式(當(dāng)然,原因之一是血緣關(guān)系)。正是由于 Spark 開發(fā)之初就考慮到支持 Mesos,因此,目前而言,Spark 運行在 Mesos 上會比運行在 YARN 上更加靈活,更加自然。用戶可選擇兩種調(diào)度模式之一運行自己的應(yīng)用程序:

粗粒度模式(Coarse-grained Mode):每個應(yīng)用程序的運行環(huán)境由一個 Dirver 和若干個 Executor 組成,其中,每個 Executor 占用若干資源,內(nèi)部可運行多個 Task(對應(yīng)多少個“slot”)。應(yīng)用程序的各個任務(wù)正式運行之前,需要將運行環(huán)境中的資源全部申請好,且運行過程中要一直占用這些資源,即使不用,最后程序運行結(jié)束后,回收這些資源。

細(xì)粒度模式(Fine-grained Mode):鑒于粗粒度模式會造成大量資源浪費,Spark On Mesos 還提供了另外一種調(diào)度模式:細(xì)粒度模式,這種模式類似于現(xiàn)在的云計算,思想是按需分配 。

7、Spark 為什么比 mapreduce 快?
        1)基于內(nèi)存計算,減少低效的磁盤交互;

        2)高效的調(diào)度算法,基于 DAG;

        3)容錯機(jī)制 Linage,精華部分就是 DAG 和 Lingae

8、簡單說一下 hadoop 和 spark 的 shuffle 相同和差異?
        1)從 high-level 的角度來看,兩者并沒有大的差別。都是將 mapper (Spark 里是 ShuffleMapTask)的輸出進(jìn)行 partition,不同的 partition 送到不同的 reducer(Spark 里 reducer 可能是下一個 stage 里的 ShuffleMapTask,也可能是 ResultTask)。Reducer 以內(nèi)存作緩沖區(qū),邊 shuffle 邊 aggregate 數(shù)據(jù),等到數(shù)據(jù) aggregate 好以后進(jìn)行 reduce() (Spark 里可能是后續(xù)的一系列操作)。

        2)從 low-level 的角度來看,兩者差別不小。Hadoop MapReduce 是 sort-based,進(jìn)入 combine() 和 reduce() 的 records 必須先 sort。這樣的好處在于 combine/reduce() 可以處理大規(guī)模的數(shù)據(jù),因為其輸入數(shù)據(jù)可以通過外排得到(mapper 對每段數(shù)據(jù)先做排序,reducer 的 shuffle 對排好序的每段數(shù)據(jù)做歸并)。目前的 Spark 默認(rèn)選擇的是 hash-based,通常使用HashMap 來對 shuffle 來的數(shù)據(jù)進(jìn)行 aggregate,不會對數(shù)據(jù)進(jìn)行提前排序。如果用戶需要經(jīng)過排序的數(shù)據(jù),那么需要自己調(diào)用類似 sortByKey() 的操作;如果你是 Spark 1.1 的用戶,可以將 spark.shuffle.manager 設(shè)置為 sort,則會對數(shù)據(jù)進(jìn)行排序。在 Spark 1.2 中,sort 將作為默認(rèn)的 Shuffle 實現(xiàn)。

        3)從實現(xiàn)角度來看,兩者也有不少差別。Hadoop MapReduce 將處理流程劃分出明顯的幾個階段:map(), spill, merge, shuffle, sort, reduce() 等。每個階段各司其職,可以按照過程式的編程思想來逐一實現(xiàn)每個階段的功能。在 Spark 中,沒有這樣功能明確的階段,只有不同的 stage 和一系列的 transformation(),所以 spill, merge, aggregate 等操作需要蘊含在 transformation() 中。如果我們將 map 端劃分?jǐn)?shù)據(jù)、持久化數(shù)據(jù)的過程稱為 shuffle write,而將reducer 讀入數(shù)據(jù)、aggregate 數(shù)據(jù)的過程稱為 shuffle read。那么在 Spark 中,問題就變?yōu)樵趺丛?job 的邏輯或者物理執(zhí)行圖中加入 shuffle write 和 shuffle read 的處理邏輯?以及兩個處理邏輯應(yīng)該怎么高效實現(xiàn)?Shuffle write 由于不要求數(shù)據(jù)有序,shuffle write 的任務(wù)很簡單:將數(shù)據(jù) partition 好,并持久化。之所以要持久化,一方面是要減少內(nèi)存存儲空間壓力,另一方面也是為了 fault-tolerance。

9. spark 工作機(jī)制
        ① 構(gòu)建 Application 的運行環(huán)境,Driver 創(chuàng)建一個 SparkContext

        ② SparkContext 向資源管理器(Standalone、Mesos、Yarn)申請Executor 資源,資源管理器啟動 StandaloneExecutorbackend(Executor)

        ③ Executor 向 SparkContext 申請 Task

        ④ SparkContext 將應(yīng)用程序分發(fā)給 Executor

        ⑤ SparkContext 就建成 DAG 圖,DAGScheduler 將 DAG 圖解析成 Stage,每個 Stage 有多個 task,形成 taskset 發(fā)送給 task Scheduler,由task Scheduler 將 Task 發(fā)送給 Executor 運行

        ⑥ Task 在 Executor 上運行,運行完釋放所有資源

10、Spark 的優(yōu)化怎么做?
        spark 調(diào)優(yōu)比較復(fù)雜,但是大體可以分為三個方面來進(jìn)行:

        1)平臺層面的調(diào)優(yōu):防止不必要的 jar 包分發(fā),提高數(shù)據(jù)的本地性,選擇高效的存儲格式如 parquet

        2)應(yīng)用程序?qū)用娴恼{(diào)優(yōu):過濾操作符的優(yōu)化降低過多小任務(wù),降低單條記錄的資源開銷,處理數(shù)據(jù)傾斜,復(fù)用 RDD 進(jìn)行緩存,作業(yè)并行化執(zhí)行等等

        3)JVM 層面的調(diào)優(yōu):設(shè)置合適的資源量,設(shè)置合理的 JVM,啟用高效的序列化方法如 kyro,增大 off head 內(nèi)存等等

11、數(shù)據(jù)本地性是在哪個環(huán)節(jié)確定的?
        具體的 task 運行在哪臺機(jī)器上,是由 dag 劃分 stage 的時候確定的

12、RDD 的彈性表現(xiàn)在哪幾點?
      1)自動的進(jìn)行內(nèi)存和磁盤的存儲切換;               

        2)基于 Lineage 的高效容錯;

        3)task 如果失敗會自動進(jìn)行特定次數(shù)的重試;

        4)stage 如果失敗會自動進(jìn)行特定次數(shù)的重試,而且只會計算失敗的分片;

        5)checkpoint 和 persist,數(shù)據(jù)計算之后持久化緩存;

        6)數(shù)據(jù)調(diào)度彈性,DAG TASK 調(diào)度和資源無關(guān);

        7)數(shù)據(jù)分片的高度彈性。

13、RDD 有哪些缺陷?
        1)不支持細(xì)粒度的寫和更新操作(如網(wǎng)絡(luò)爬蟲),spark 寫數(shù)據(jù)是粗粒度的。所謂粗粒度,就是批量寫入數(shù)據(jù),為了提高效率。但是讀數(shù)據(jù)是細(xì)粒度的也就是說可以一條條的讀 。

        2)不支持增量迭代計算,但是 Flink 支持

14、Spark 的 shuffle 過程?
        可以從下面三點去展開:

shuffle 過程的劃分
shuffle 的中間結(jié)果如何存儲
shuffle 的數(shù)據(jù)如何拉取過來
15、Spark 的數(shù)據(jù)本地性有哪幾種?
        Spark 中的數(shù)據(jù)本地性有三種:

PROCESS_LOCAL 是指讀取緩存在本地節(jié)點的數(shù)據(jù)
NODE_LOCAL 是指讀取本地節(jié)點硬盤數(shù)據(jù)
ANY 是指讀取非本地節(jié)點數(shù)據(jù)
        通常讀取數(shù)據(jù) PROCESS_LOCAL>NODE_LOCAL>ANY,盡量使數(shù)據(jù)以 PROCESS_LOCAL 或 NODE_LOCAL 方式讀取。其中 PROCESS_LOCAL 還和 cache 有關(guān),如果 RDD 經(jīng)常用的話將該 RDD cache 到內(nèi)存中,注意,由于cache 是 lazy 的,所以必須通過一個 action 的觸發(fā),才能真正的將該 RDD cache 到內(nèi)存中。

16、Spark 為什么要持久化,一般什么場景下要進(jìn)行 persist 操作?
         為什么要進(jìn)行持久化?

         spark 所有復(fù)雜一點的算法都會有 persist 身影,spark 默認(rèn)數(shù)據(jù)放在內(nèi)存,spark 很多內(nèi)容都是放在內(nèi)存的,非常適合高速迭代,1000 個步驟只有第一個輸入數(shù)據(jù),中間不產(chǎn)生臨時數(shù)據(jù),但分布式系統(tǒng)風(fēng)險很高,所以容易出錯,就要容錯,rdd 出錯或者分片可以根據(jù)血統(tǒng)算出來,如果沒有對父 rdd 進(jìn)行 persist 或者 cache 的話,就需要重頭做。正常情況下,以下場景會使用 persist:

某個步驟計算非常耗時,需要進(jìn)行 persist 持久化
計算鏈條非常長,重新恢復(fù)要算很多步驟,很好使,persist
checkpoint 所在的 rdd 要持久化 persist。checkpoint 前,要持久化,寫個 rdd.cache 或者 rdd.persist,將結(jié)果保存起來,再寫 checkpoint 操作,這樣執(zhí)行起來會非??欤恍枰匦掠嬎?rdd 鏈條了。checkpoint 之前一定會進(jìn)行 persist 。
shuffle 之后要 persist,shuffle 要進(jìn)行網(wǎng)絡(luò)傳輸,風(fēng)險很大,數(shù)據(jù)丟失重來,恢復(fù)代價很大
shuffle 之前進(jìn)行 persist,框架默認(rèn)將數(shù)據(jù)持久化到磁盤,這個是框架自動做的
17.介紹一下 join 操作優(yōu)化經(jīng)驗?
         join 其實常見的就分為兩類:map-side join 和 reduce-side join。當(dāng)大表和小表 join 時,用 map-side join 能顯著提高效率。將多份數(shù)據(jù)進(jìn)行關(guān)聯(lián)是數(shù)據(jù)處理過程中非常普遍的用法,不過在分布式計算系統(tǒng)中,這個問題往往會變得非常麻煩,因為框架提供的 join 操作一般會將所有數(shù)據(jù)根據(jù) key 發(fā)送到所有的 reduce 分區(qū)中去,也就是 shuffle 的過程。造成大量的網(wǎng)絡(luò)以及磁盤 IO 消耗,運行效率極其低下,這個過程一般被稱為 reduce-side-join。如果其中有張表較小的話,我們則可以自己實現(xiàn)在 map 端實現(xiàn)數(shù)據(jù)關(guān)聯(lián),跳過大量數(shù)據(jù)進(jìn)行 shuffle 的過程,運行時間得到大量縮短,根據(jù)不同數(shù)據(jù)可能會有幾倍到數(shù)十倍的性能提升 。

18、描述 Yarn 執(zhí)行一個任務(wù)的過程?
客戶端 client 向 ResouceManager 提交 Application,ResouceManager 接受 Application 并根據(jù)集群資源狀況選取一個 node 來啟動 Application 的任務(wù)調(diào)度器 driver(ApplicationMaster)
ResouceManager 找到那個 node,命令其該 node 上的 nodeManager來啟動一個新的 JVM 進(jìn)程運行程序的 driver(ApplicationMaster)部分,driver(ApplicationMaster)啟動時會首先向 ResourceManager 注冊,說明由自己來負(fù)責(zé)當(dāng)前程序的運行
driver(ApplicationMaster)開始下載相關(guān) jar 包等各種資源,基于下載的 jar 等信息決定ResourceManager 申請具體的資源內(nèi)容。
ResouceManager 接受到 driver(ApplicationMaster)提出的申請后,會最大化地滿足 資源分配請求,并發(fā)送資源的元數(shù)據(jù)信息給 driver(ApplicationMaster)
driver(ApplicationMaster)收到發(fā)過來的資源元數(shù)據(jù)信息后會根據(jù)元數(shù)據(jù)信息發(fā)指令給具體機(jī)器上的NodeManager,讓其啟動具體的 container。
NodeManager 收到 driver 發(fā)來的指令,啟動 container,container 啟動后必須向 driver(ApplicationMaster)注冊。
driver(ApplicationMaster)收到 container 的注冊,開始進(jìn)行任務(wù)的調(diào)度和計算,直到 任務(wù)完成。
        需要注意一個細(xì)節(jié):注意:如果 ResourceManager 第一次沒有能夠滿足 driver(ApplicationMaster)的資源請求 ,后續(xù)發(fā)現(xiàn)有空閑的資源,會主動向 driver(ApplicationMaster)發(fā)送可用資源的元數(shù)據(jù)信息以提供更多的資源用于當(dāng)前程序的運行。

19、Spark on Yarn 模式有哪些優(yōu)點?
與其他計算框架共享集群資源(Spark 框架與 MapReduce 框架同時運行,如果不用 Yarn 進(jìn)行資源分配,MapReduce 分到的內(nèi)存資源會很少,效率低下);資源按需分配,進(jìn)而提高集群資源利用等 。
相較于 Spark 自帶的 Standalone 模式,Yarn 的資源分配更加細(xì)致 。
Application 部署簡化,例如 Spark,Storm 等多種框架的應(yīng)用由客戶端提交后,由 Yarn 負(fù)責(zé)資源的管理和調(diào)度,利用 Container 作為資源隔離的單位,以它為單位去使用內(nèi)存,cpu 等 。
Yarn 通過隊列的方式,管理同時運行在 Yarn 集群中的多個服務(wù),可根據(jù)不同類型的應(yīng)用程序負(fù)載情況,調(diào)整對應(yīng)的資源使用量,實現(xiàn)資源彈性管理 。
20、談?wù)勀銓?container 的理解?
Container 作為資源分配和調(diào)度的基本單位,其中封裝了的資源如內(nèi)存,CPU,磁盤,網(wǎng)絡(luò)帶寬等。目前 yarn 僅僅封裝內(nèi)存和 CPU 。
Container 由 ApplicationMaster 向 ResourceManager 申請的,由 ResouceManager 中的資源調(diào)度器異步分配給 ApplicationMaster
Container 的運行是由 ApplicationMaster 向資源所在的 NodeManager 發(fā)起的,Container 運行時需提供內(nèi)部執(zhí)行的任務(wù)命令
21、Spark 使用 parquet 文件存儲格式能帶來哪些好處?
如果說 HDFS 是大數(shù)據(jù)時代分布式文件系統(tǒng)首選標(biāo)準(zhǔn),那么 parquet 則是整個大數(shù)據(jù)時代文件存儲格式實時首選標(biāo)準(zhǔn) 。
速度更快:從使用 spark sql 操作普通文件 CSV 和 parquet 文件速度對比上看,絕大多數(shù)情況會比使用 csv 等普通文件速度提升 10 倍左右,在一些普通文件系統(tǒng)無法在 spark 上成功運行的情況下,使用 parquet 很多時候可以成功運行。
parquet 的壓縮技術(shù)非常穩(wěn)定出色,在 spark sql 中對壓縮技術(shù)的處理可能無法正常的完成工作(例如會導(dǎo)致 lost task,lost executor)但是此時如果使用 parquet 就可以正常的完成
極大的減少磁盤 I/o,通常情況下能夠減少 75%的存儲空間,由此可以極大的減少 spark sql 處理數(shù)據(jù)的時候的數(shù)據(jù)輸入內(nèi)容,尤其是在 spark1.6x 中有個下推過濾器在一些情況下可以極大的減少磁盤的 IO 和內(nèi)存的占用。
spark 1.6x 中 parquet 方式極大的提升了掃描的吞吐量,極大提高了數(shù)據(jù)的查找速度 spark1.6 和 spark1.5x 相比而言,提升了大約 1 倍的速度,在spark1.6X 中,操作 parquet 時候 cpu 也進(jìn)行了極大的優(yōu)化,有效的降低了cpu 消耗。
采用 parquet 可以極大地優(yōu)化 spark 的調(diào)度和執(zhí)行。經(jīng)過測試 spark 如果用 parquet 可以有效的減少 stage 的執(zhí)行消耗,同時可以優(yōu)化執(zhí)行路徑
22、介紹 parition 和 block 有什么關(guān)聯(lián)關(guān)系?
hdfs 中的 block 是分布式存儲的最小單元,等分,可設(shè)置冗余,這樣設(shè)計有一部分磁盤空間的浪費,但是整齊的 block 大小,便于快速找到、讀取對應(yīng)的內(nèi)容;
Spark 中的 partion 是彈性分布式數(shù)據(jù)集 RDD 的最小單元,RDD 是由分布在各個節(jié)點上的 partion 組成的。partion 是指的 spark 在計算過程中,生成的數(shù)據(jù)在計算空間內(nèi)最小單元,同一份數(shù)據(jù)(RDD)的 partion 大小不一,數(shù)量不定,是根據(jù) application 里的算子和最初讀入的數(shù)據(jù)分塊數(shù)量決定;
block 位于存儲空間、partion 位于計算空間,block 的大小是固定的、partion 大小是不固定的,是從 2 個不同的角度去看數(shù)據(jù)
23、Spark 應(yīng)用程序的執(zhí)行過程是什么?
構(gòu)建 Spark Application 的運行環(huán)境(啟動 SparkContext),SparkContext 向資源管理器(可以是 Standalone、Mesos 或 YARN)注冊并申請運行 Executor 資源;
資源管理器分配 Executor 資源并啟動 StandaloneExecutorBackend,Executor 運行情況將隨著心跳發(fā)送到資源管理器上;
SparkContext 構(gòu)建成 DAG 圖,將 DAG 圖分解成 Stage,并把 Taskset發(fā)送給 Task Scheduler。Executor 向 SparkContext 申請 Task,Task Scheduler 將 Task 發(fā)放給 Executor 運行同時 SparkContext 將應(yīng)用程序代碼發(fā)放給 Executor;
Task 在 Executor 上運行,運行完畢釋放所有資源 。
24、不需要排序的 hash shuffle 是否一定比需要排序的 sort shuffle 速度快?
        不一定,當(dāng)數(shù)據(jù)規(guī)模小,Hash shuffle 快于 Sorted Shuffle ;數(shù)據(jù)規(guī)模大的時候,sorted Shuffle 會比 Hash shuffle 快很多 。因為數(shù)量大的有很多小文件,不均勻,甚至出現(xiàn)數(shù)據(jù)傾斜,消耗內(nèi)存大,1.x 之前 spark 使用 hash,適合處理中小規(guī)模,1.x 之后,增加了 Sorted shuffle,Spark 更能勝任大規(guī)模處理了 。

25、Sort-based shuffle 的缺陷?
如果 mapper 中 task 的數(shù)量過大,依舊會產(chǎn)生很多小文件,此時在 shuffle 傳遞數(shù)據(jù)的過程中 reducer 段,reduce 會需要同時大量的記錄進(jìn)行反序列化,導(dǎo)致大量的內(nèi)存消耗和 GC 的巨大負(fù)擔(dān),造成系統(tǒng)緩慢甚至崩潰。
如果需要在分片內(nèi)也進(jìn)行排序,此時需要進(jìn)行 mapper 段和 reducer 段的兩次排序。
26、spark.storage.memoryFraction 參數(shù)的含義,實際生產(chǎn)中如何調(diào)優(yōu)?
用于設(shè)置 RDD 持久化數(shù)據(jù)在 Executor 內(nèi)存中能占的比例,默認(rèn)是 0.6,,默認(rèn) Executor 60%的內(nèi)存,可以用來保存持久化的 RDD 數(shù)據(jù)。根據(jù)你選擇的不同的持久化策略,如果內(nèi)存不夠時,可能數(shù)據(jù)就不會持久化,或者數(shù)據(jù)會寫入磁盤;
如果持久化操作比較多,可以提高 spark.storage.memoryFraction 參數(shù),使得更多的持久化數(shù)據(jù)保存在內(nèi)存中,提高數(shù)據(jù)的讀取性能,如果 shuffle 的操作比較多,有很多的數(shù)據(jù)讀寫操作到 JVM 中,那么應(yīng)該調(diào)小一點,節(jié)約出更多的內(nèi)存給 JVM,避免過多的 JVM gc 發(fā)生。在 WEB UI 中觀察如果發(fā)現(xiàn) gc 時間很長,可以設(shè)置 spark.storage.memoryFraction 更小一點。
小結(jié)
        本篇文章總結(jié)了在大數(shù)據(jù)開發(fā)面試過程中關(guān)于 Spark 的核心知識點 。大家不用去硬記,但至少被問到了需要有印象且能按照正確的思路去回答 。下周我將為大家?guī)砀佑埠说母韶洠瑲g迎持續(xù)關(guān)注,我們下期不見不散!

作者:夢想家 Alex


歡迎關(guān)注:大數(shù)據(jù)夢想家