2021-02-21

13面試問題(2)

是否存在i+1<i的數(shù)

存在,int最大值

redis的五種數(shù)據類型是指

value的類型,key只能是string類型

企業(yè)站內搜索技術選型

單獨使用Lucene實現(xiàn)站內搜索需要開發(fā)的工作量較大,因此不建議采用。

solr是基于Lucene的全文搜索服務器,能滿足搜索技術

esearch是更加新的技術

4、解釋內存溢出和內存泄漏,不用的對象需要置為null嗎?

內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用

內存泄露 memory leak,是指程序在申請內存后,無法釋放已申請的內存空間

內存泄露會導致內存溢出

需要,能釋放內存空間

定時任務

直接修改/etc/crontab文件

每星期日的6:30執(zhí)行l(wèi)s命令:

30 6 * * 0 ls

每15分鐘執(zhí)行一次ls命令:

*/15 * * * * ls

幾種垃圾回收機制

標記-清除收集器

標記-壓縮收集器

復制收集器

Fs命令與dfs命令有什么不同的地方

fs可以操作任何文件系統(tǒng)

dfs:只能操作HDFS文件系統(tǒng)


10大數(shù)據習題集

1.kafka集群的規(guī)模,消費速度是多少。

答:一般中小型公司是10個節(jié)點,每秒20M左右。

hadoop的shuffle過程

一、Map端的shuffle

  Map端會處理輸入數(shù)據并產生中間結果,這個中間結果會寫到本地磁盤,而不是HDFS。每個Map的輸出會先寫到內存緩沖區(qū)中,當寫入的數(shù)據達到設定的閾值時,系統(tǒng)將會啟動一個線程將緩沖區(qū)的數(shù)據寫到磁盤,這個過程叫做spill。

  在spill寫入之前,會先進行二次排序,首先根據數(shù)據所屬的partition進行排序,然后每個partition中的數(shù)據再按key來排序。partition的目是將記錄劃分到不同的Reducer上去,以期望能夠達到負載均衡,以后的Reducer就會根據partition來讀取自己對應的數(shù)據。接著運行combiner(如果設置了的話),combiner的本質也是一個Reducer,其目的是對將要寫入到磁盤上的文件先進行一次處理,這樣,寫入到磁盤的數(shù)據量就會減少。最后將數(shù)據寫到本地磁盤產生spill文件(spill文件保存在{mapred.local.dir}指定的目錄中,Map任務結束后就會被刪除)。

  最后,每個Map任務可能產生多個spill文件,在每個Map任務完成前,會通過多路歸并算法將這些spill文件歸并成一個文件。至此,Map的shuffle過程就結束了。

二、Reduce端的shuffle

  Reduce端的shuffle主要包括三個階段,copy、sort(merge)和reduce。

  首先要將Map端產生的輸出文件拷貝到Reduce端,但每個Reducer如何知道自己應該處理哪些數(shù)據呢?因為Map端進行partition的時候,實際上就相當于指定了每個Reducer要處理的數(shù)據(partition就對應了Reducer),所以Reducer在拷貝數(shù)據的時候只需拷貝與自己對應的partition中的數(shù)據即可。每個Reducer會處理一個或者多個partition,但需要先將自己對應的partition中的數(shù)據從每個Map的輸出結果中拷貝過來。

  接下來就是sort階段,也成為merge階段,因為這個階段的主要工作是執(zhí)行了歸并排序。從Map端拷貝到Reduce端的數(shù)據都是有序的,所以很適合歸并排序。最終在Reduce端生成一個較大的文件作為Reduce的輸入。

  最后就是Reduce過程了,在這個過程中產生了最終的輸出結果,并將其寫到HDFS上。

5.spark streming在實時處理時會發(fā)生什么故障,如何停止

StreamingContext.stop會把關聯(lián)的SparkContext對象也停止,如果不想把SparkContext對象也停止的話可以把StremingContext.stop的可選參數(shù)stopSparkContext設為flase。

7.說一下你對hadoop生態(tài)圈的認識。

說一下大數(shù)據PPT

8.yarn的理解:

ResourceManager:負責整個集群的資源管理和調度 ApplicationMaster:負責應用程序相關事務,比如任務調度、任務監(jiān)控和容錯等。 目前可以支持多種計算框架運行在YARN上面,比如MapReduce、storm、Spark、Flink。

6、兩個文件合并的問題:給定a、b兩個文件,各存放50億個url,每個url各占用64字節(jié),內存限制是4G,如何找出a、b文件共同的url?

? 1)主要的思想是把文件分開進行計算,在對每個文件進行對比,得出相同的URL,因為以上說是含有相同的URL所以不用考慮數(shù)據傾斜的問題。詳細的解題思路如下:

? ? a、可以估計每個文件的大小為5G*64=300G,遠大于4G。所以不可能將其完全加載到內存中處理??紤]采取分而治之的方法。

? ? b、遍歷文件a,對每個url求取hash(url)%1000,然后根據所得值將url分別存儲到1000個小文件(設為a0,a1,...a999)當中。這樣每個小文件的大小約為300M。

? ? b、遍歷文件b,采取和a相同的方法將url分別存儲到1000個小文件(b0,b1....b999)中。這樣處理后,所有可能相同的url都在對應的小文件(a0 vs b0, a1 vs b1....a999 vs b999)當中,不對應的小文件(比如a0 vs b99)不可能有相同的url。然后我們只要求出1000對小文件中相同的url即可。

? ? c、比如對于a0 vs b0,我們可以遍歷a0,將其中的url存儲到hash_map當中。然后遍歷b0,如果url在hash_map中,則說明此url在a和b中同時存在,保存到文件中即可。

? ? d、如果分成的小文件不均勻,導致有些小文件太大(比如大于2G),可以考慮將這些太大的小文件再按類似的方法分成小小文件即可

7、按照需求使用spark編寫一下程序?

? A、當前文件a.text的格式如下,請統(tǒng)計每個單詞出現(xiàn)的個數(shù)和第四列每個元素出現(xiàn)的個數(shù)

A,b,c,d

B,b,f,e

A,a,c,f

sc.textFile(“a.text”).flatMap(_.split(“,”)).map((_,1)).ReduceByKey(_+_).foreach(println)

sc.textFile(“a.text”).map(line=>{(line.split(",")(3),1)}).reduceByKey(_+_).foreach(println)

? B、HDFS中有兩個文件a.text與b.text,文件的格式為(ip,username),如:a.text,b.text

a.text

127.0.0.1? xiaozhang

127.0.0.1? xiaoli

127.0.0.2? wangwu

127.0.0.3? lisi

B.text

127.0.0.4? lixiaolu

127.0.0.5? lisi

每個文件至少有1000萬行,請用程序完成以下工作,

1)出現(xiàn)在b.text而沒有出現(xiàn)在a.text的IP

rdd22.filter(x=>{!x.contains(rdd11)}).foreach(println)

8.kafka 重啟是否會導致數(shù)據丟失

不會 因為kafka會做持久化,并且offset會保存在zk中,重啟后每次讀取都會從kafka最新的offset讀?。梢詮膠k里面指定kafka的offset實現(xiàn)從指定位置讀?。?/p>

9.講講checkpoint

checkpoint的作用就是將DAG中比較重要的中間數(shù)據做一個檢查點將結果存儲到一個高可用的地方(通常這個地方就是HDFS里面),防止很長的計算過程出錯時還要從頭計算,并且為整合歷史數(shù)據提供了可能

10.spark streaming 的優(yōu)缺點

和storm與flink對比來說

11.kafka的message包括哪些信息

一個Kafka的Message由一個固定長度的header和一個變長的消息體body組成

12.怎么查看kafka的offset

consumer.position()

13.?rdd 怎么分區(qū)寬依賴和窄依賴

寬依賴:父RDD的分區(qū)被子RDD的多個分區(qū)使用? ?例如 groupByKey、reduceByKey、sortByKey等操作會產生寬依賴,會產生shuffle

窄依賴:父RDD的每個分區(qū)都只被子RDD的一個分區(qū)使用? 例如map、filter、union等操作會產生窄依賴

14.怎么解決kafka的數(shù)據丟失

producer端:

宏觀上看保證數(shù)據的可靠安全性,肯定是依據分區(qū)數(shù)做好數(shù)據備份,設立副本數(shù)。

broker端:

topic設置多分區(qū),分區(qū)自適應所在機器,為了讓各分區(qū)均勻分布在所在的broker中,分區(qū)數(shù)要大于broker數(shù)。

分區(qū)是kafka進行并行讀寫的單位,是提升kafka速度的關鍵。

Consumer端

consumer端丟失消息的情形比較簡單:如果在消息處理完成前就提交了offset,那么就有可能造成數(shù)據的丟失。由于Kafka consumer默認是自動提交位移的,所以在后臺提交位移前一定要保證消息被正常處理了,因此不建議采用很重的處理邏輯,如果處理耗時很長,則建議把邏輯放到另一個線程中去做。為了避免數(shù)據丟失,現(xiàn)給出兩點建議:

enable.auto.commit=false? 關閉自動提交位移

在消息被完整處理之后再手動提交位移

3.0請寫出以下的shell命令

(1)殺死一個job

(2)刪除hdfs上的 /tmp/aaa目錄

答:(1)hadoop job –list 得到job的id,然后執(zhí) 行 hadoop job -kill jobId就可以殺死一個指定jobId的job工作了。

(2)hadoopfs -rmr /tmp/aaa

12.0 請簡述mapreduce中的combine和partition的作用

答:combiner是發(fā)生在map的最后一個階段,其原理也是一個小型的reducer,主要作用是減少輸出到reduce的數(shù)據量,緩解網絡傳輸瓶頸,提高reducer的執(zhí)行效率。

partition的主要作用將map階段產生的所有kv對分配給不同的reducer task處理,可以將reduce階段的處理負載進行分攤

31.數(shù)據的三范式

答:

第一范式()無重復的列

第二范式(2NF)屬性完全依賴于主鍵 [消除部分子函數(shù)依賴]

第三范式(3NF)屬性不依賴于其它非主屬性 [消除傳遞依賴]

35.MapReduce優(yōu)化經驗(即hive調優(yōu)、即hadoop調優(yōu))

答:(1.)設置合理的map和reduce的個數(shù)。合理設置blocksize

(2.)避免出現(xiàn)數(shù)據傾斜

(4.對數(shù)據進行壓縮

(5.小文件處理優(yōu)化:事先合并成大文件,用MR中的combineTextInputformat

103. hive 中的壓縮格式 RCFile、TextFile、SequenceFile各有什么區(qū)別?

其余兩個都進行了壓縮,但是不可以直接打開,在創(chuàng)建hive表時需要指定文件格式才能使用

14、有可能使hadoop任務輸出到多個目錄中嗎?如果可以,怎么做?

自定義outputformat或者用multioutputs工具

四、 你們數(shù)據庫怎么導入hive 的,有沒有出現(xiàn)問題

像blob或clob之類的數(shù)據,采取不抽取

增量全量導入配置的參數(shù)不同

空值問題、分隔符問題

infa導入時數(shù)據長度問題

sqoop導入時還要注意字段類型轉換問題

五、hdfs-site.xml的3個主要屬性?

dfs.name.dir決定的是元數(shù)據存儲的路徑以及DFS的存儲方式(磁盤或是遠端)

dfs.data.dir決定的是數(shù)據存儲的路徑

fs.checkpoint.dir用于第二Namenode

八、hadoop和spark都是并行計算,那么他們有什么相同和區(qū)別?

兩者都使用mr模型來進行并行計算,hadoop的一個作業(yè)稱為job,job里面分為map task和reduce task,每個task都是在自己的進程中運行的,當task結束時,進程也會結束。

Spark用戶提交的任務稱為application,一個application對應一個SparkContext,app中存在多個job,沒觸發(fā)一個action操作就會產生一個job。

這些job可以并行或者串行執(zhí)行,每個job有多個stage,stage是shuffle過程中DAGSchaduler通過RDD之間的依賴關系劃分job而來的,每個stage里面有多個task,組成taskset有TaskSchaduler分發(fā)到各個executor中執(zhí)行,executor的生命周期是和application一樣的,即使沒有job運行也是存在的,所以task可以快速啟動讀取內存進行計算的。

Hadoop的job只有map和reduce操作,表達能力比較欠缺而且在mr過程中會重復的讀寫hdfs,造成大量的io操作,多個job需要自己管理關系。

Spark的迭代計算都是在內存中進行的,API中提供了大量的RDD操作join,groupby等,而且通過DAG圖可以實現(xiàn)良好的容錯。

十一、簡單說一下hadoop和spark的shuffle過程

Hadoop:map端保存分片數(shù)據,通過網絡收集到reduce端。

Spark:spark的shuffle實在DAGSchedular劃分Stage的時候產生的,TaskSchedular要分發(fā)Stage到各個worker的executor。減少shuffle可以提高性能。

十六、請列出正常的hadoop集群中hadoop都分別需要啟動 哪些進程,他們的作用分別都是什么,請盡量列的詳細一些。

namenode:負責管理hdfs中文件塊的元數(shù)據,響應客戶端請求,管理datanode上文件block的均衡,維持副本數(shù)量

Secondname:主要負責做checkpoint操作;也可以做冷備,對一定范圍內數(shù)據做快照性備份。

Datanode:存儲數(shù)據塊,負責客戶端對數(shù)據塊的io請求

Jobtracker :管理任務,并將任務分配給 tasktracker。

Tasktracker: 執(zhí)行JobTracker分配的任務。

Resourcemanager、Nodemanager、Journalnode、Zookeeper、Zkfc

1.Kudu優(yōu)勢在于:

提供快速的全量數(shù)據分析與實時處理功能

結構化的數(shù)據模型,支持標準SQL語法,支持數(shù)據的更新操作。

集成Impala,利用Imapla SQL語法可操作Kudu數(shù)據。

與mapreduce、spark以及其它hadoop生態(tài)系統(tǒng)集成。

利用Cloudera Manager,方便管理和維護。

高可用。Tablet Servers and Masters利用Raft Consensus Algorithm.確保只要有一半的副本可用,則tablet可用(可讀寫)。

對數(shù)據順序掃描(scan)和隨機訪問(random access)同時具有高性能,簡化用戶復雜的混合架構。

2.kudu的基本結構

Table

Table就是你在Kudu里存儲數(shù)據的地方,可以理解為一個表

Tablet

Tablet是存儲的最小單位,類似HDFS的block,HBase的region。

Tablet Server

類似HDFS的DataNode, tablet server上存了多個Tablet,每個Tablet有多個副本存放在不同的Table Server上,每個Tablet副本同時只有一個Leader,Leader對用戶提供寫操作,然后同步給其它follower,其它follower只提供讀服務,不提供寫服務。當Leader節(jié)點發(fā)生故障后,通過算法 Raft Consensus Algorithm來重新選舉Leader節(jié)點。

Master Server

類似HDFS的NameNode, Master負責管理元數(shù)據。這些元數(shù)據包括talbet的基本信息,位置信息。Master還作為負載均衡服務器,監(jiān)聽Tablet Server的健康狀態(tài)

Catalog Table

Catalog table存儲著元數(shù)據信息:

3.Spark Streaming的三種運用場景

1、無狀態(tài)操作

???????? 只關注當前新生成的小批次數(shù)據,所有計算都只是基于這個批次的數(shù)據進行處理。

2、狀態(tài)操作

???????? 除了當前新生成的小批次數(shù)據,但還需要用到以前所生成的所有的歷史數(shù)據

3、window操作

???????? 關注窗口內的批次數(shù)據

7.? ? ? “jps”命令的用處?

解答:

這個命令可以檢查Namenode、Datanode、Task Tracker、 Job Tracker是否正常工作。

12.??請列出你所知道的 hadoop 調度器,并簡要說明其工作方法?

解答:

1.FIFO schedular:默認,先進先出的原則

2.Capacity schedular:計算能力調度器,選擇占用最小,優(yōu)先級高的先執(zhí)行,以此類推。

3.Fair schedular:公平調度,所有的job具有相同的資源。

22.??hadoop 的 namenode 宕機,怎么解決

解答:

先分析宕機后的損失,宕機后直接導致client無法訪問,內存中的元數(shù)據丟失,但是硬盤中的元數(shù)據應該還存在,如果只是節(jié)點掛了,重啟即可,如果是機器掛了,重啟機器后看節(jié)點是否能重啟,不能重啟就要找到原因修復了。但是最終的解決方案應該是在設計集群的初期就考慮到這個問題,做namenode的HA。

27.? 在hadoop中文件的壓縮帶來了兩大好處:

解答:

(1)它減少了存儲文件所需的空間;

(2)加快了數(shù)據在網絡上或者從磁盤上或到磁盤上的傳輸速度;

28.java類型如何轉化為hadoop基本類型?

調用hadoop類型的構造方法,或者調用set()方法。

new LongWritable(123L);

29.hadoop基本類型如何轉化為java類型?

對于Text,需要調用toString()方法,其他類型調用get()方法。

3.? ? ? 生產環(huán)境中為什么建議使用外部表?

解答:

1、因為外部表不會加載數(shù)據到hive,減少數(shù)據傳輸、數(shù)據還能共享。

2、hive不會修改數(shù)據,所以無需擔心數(shù)據的損壞

3、刪除表時,只刪除表結構、不刪除數(shù)據。

8.??????假如一個分區(qū)的數(shù)據錯誤怎么通過hivesql刪除

解答:

alter table ptable drop partition (daytime='20140911',city='bj');

元數(shù)據,數(shù)據文件都刪除,但目錄daytime= 20140911還在

9.? ? ? Hive里面用什么代替in查詢

解答:

提示:Hive中的left semi join替換sql中的in操作

7.??????HBase的檢索支持3種方式:

解答:

(1) 通過單個Rowkey訪問,即按照某個Rowkey鍵值進行get操作,這樣獲取唯一一條記錄;

(2) 通過Rowkey的range進行scan,即通過設置startRowKey和endRowKey,在這個范圍內進行掃描。這樣可以按指定的條件獲取一批記錄;

(3) 全表掃描,即直接掃描整張表中所有行記錄。


08hadoop生態(tài)圈復習筆記

Zookeeper概述

Zookeeper介紹

Zookeeper是分布式應用程序的協(xié)調服務框架,是Hadoop的重要組件。ZK要解決的問題:

1.分布式環(huán)境下的數(shù)據一致性。

2.分布式環(huán)境下的統(tǒng)一命名服務

3.分布式環(huán)境下的配置管理

4.分布式環(huán)境下的分布式鎖

5.集群管理問題

分布式編程容易出現(xiàn)的問題

1.活鎖:

多個線程爭用一個資源,但是沒有任何一個 線程能拿到這個資源。如此循環(huán)往復,就形成了活鎖?;铈i會耗盡Cpu資源(在做無意義的調度)

2.死鎖:

有一個線程拿到資源,但相互等待互不釋放造成死鎖

3.需要考慮集群的管理問題:

需要有一套機制來檢測到集群里節(jié)點的狀態(tài)變化。zk是通過臨時節(jié)點監(jiān)控哪個服務器掛掉的?。。。。?!

4.如果用一臺機器做集群管理:

存在單點故障問題,所以針對集群管理,也需要形成一個集群(奇數(shù)臺,至少3臺)

5.管理集群里Leader的選舉問題(要根據一定的算法和規(guī)則來選舉),包括要考慮Leader掛掉之后,如何從剩余的follower里選出Leader

6.分布式鎖的實現(xiàn),用之前學的重入鎖,同步代碼塊是做不了的(因為之前學的鎖都是單機鎖)

Zk數(shù)據結構

1.ZK有一個最開始的節(jié)點

2.每個znode節(jié)點都可存儲數(shù)據

3.Znode樹的維系實在內存中,目的是供用戶快速的查詢

4.每個znode節(jié)點都是一個路徑(通過路徑來定位這個節(jié)點)

5.每個路徑名都是唯一的。

想要執(zhí)行以下指令,需要先啟動zk服務器端(bash zkServer.sh start),再啟動zk客戶端(bash zkCli.sh)

ZK指令

ls/create/delete/get/set

Zk集群搭建完畢后的數(shù)據一致性:

如果用java代碼連接該集群中的任何一個服務器,對其做的節(jié)點上的修改,在其余服務器也會產生同樣的修改(即:集群中的服務器保證數(shù)據完全一致)

Zookeeper事務概念?

1.每一個創(chuàng)建節(jié)點、修改節(jié)點、刪除節(jié)點操作都是一個事務,每一個事務都用一個事務id來代表,叫:zxid

2.zxid 是全局唯一,并且全局遞增的。作用就是可以根據最大事務id,找到最新的事務

Zookeeper選舉機制

Leader選舉出來之后

Leader身上一般是有最新數(shù)據的(有最大事務id的),所以首先做的就是原子廣播(通過原子廣播端口發(fā)送事物給其余服務器)。

原子廣播的目的:

一、是為了確保數(shù)據一致性(即客戶端無論通過哪個zk服務器查看數(shù)據,數(shù)據都是一樣的)

二、是為了防止leader掛掉之后,數(shù)據的丟失問題

節(jié)點類型及特點

persistent(持久節(jié)點):

ephemeral(臨時節(jié)點):

如果和他綁定的服務器宕機了,他則消失

觀察者模式

觀察者模式的產生意義:

為了提高Zk選舉性能,處理思想就是減少投票人數(shù)。(前提是滿足過半機制),

由此,zk引出了觀 察者模式 觀察者特點:

1.不參與投票(選舉的投票以及事務更新的投票)

2.觀察者會跟隨最后的投票結果

Zookeeper的特性

數(shù)據一致性(單一視圖)

client不論連接到哪個Zookeeper,展示給它都是同一個視圖,即查詢的數(shù)據都是一樣的。這是zookeeper最重要的性能。

原子性

對于事務決議的更新,要么都更新成功,要么都不更新。

可靠性

一旦服務端發(fā)生改變,那么這次變更將會一直保留下來,除非有另一個事務又對其進行了改變。

實時性

Zookeeper保證客戶端將在非常短的時間間隔范圍內獲得服務器的更新信息,或者服務器失效 的信息,或者指定監(jiān)聽事件的變化信息。(前提條件是:網絡狀況良好)

過半性 zookeeper

集群必須有半數(shù)以上的機器存活才能正常工作。因為只有滿足過半數(shù),才能滿足選 舉機制選出Leader。因為只有過半,在做事務決議時,事務才能更新。 所以一般來說,zookeeper集群的數(shù)量最好是奇數(shù)個。

Zk的腦裂

腦裂的定義:

在管理集群里,出現(xiàn)兩個Leader的狀況,造成數(shù)據混亂,造成整個集群出現(xiàn)問題。腦裂問題是不可控和不可模擬的。出現(xiàn)腦裂問題的根源是選舉機制自集群的選舉。 并且普遍存在于Master-slave架構(主從架構)里。

Zk腦裂的預防 :

為選舉的Leader分配遞增id,根據id的大小去判斷是否老Leader或新Leader,如果是老Leader, 就不接受其指令。

hadoop

什么是Hadoop?

Hadoop是將海量數(shù)據在分布式集群上儲存(通過HDFS(分布式數(shù)據儲存系統(tǒng))),并運行分布式分析數(shù)據(基于mapreduce的一套數(shù)據處理框架)

Hadoop能儲存和抽取非關系型數(shù)據,但并沒有查詢語言介入,因此不能說它是一個數(shù)據庫,它更像一個數(shù)據倉庫,只有通過mapreduce這樣的工具才能進行真正的數(shù)據處理

HDFS的特點

HDFS概述(HDFS架構圖):

HDFS中存在:

一個名字節(jié)點NameNode(對應一臺服務器)和多個數(shù)據節(jié)點DataNode(對應多臺服務器)

NameNode

存儲元數(shù)據信息(元數(shù)據:描述數(shù)據的數(shù)據)

元數(shù)據保存在內存/磁盤中

保存文件、block、datanode之間的映射關系

DataNode

存儲block內容

存儲在磁盤中

HDFS優(yōu)點

1.支持超大文件。

2.檢測和快速應對硬件故障

3.使應用程序能以流的形式訪問數(shù)據集,增大了數(shù)據的吞吐量

4.大部分hdfs操作文件時,需要一次寫入,多次讀取,不會修改,有利于提高吞吐量

5.高容錯性(數(shù)據自動保存多個副本,副本丟失后自動恢復)

6.可構建在廉價機器上,提高集群存儲能力

HDFS缺點

不適用于低延遲數(shù)據訪問的場景(因為提高了數(shù)據吞吐量,而犧牲了獲取數(shù)據的延遲)

不適用于大量的小文件儲存的場景(namenode的內存大小,決定了hdfs文件系統(tǒng)可保存的文件數(shù)量。大量的小文件會在namenode上產生大量的記錄,占用其空間)

不適用于多用戶寫入文件、修改文件的場景,只有這樣數(shù)據的吞吐量才能大。

不支持超強的事務(沒有像關系型數(shù)據庫那樣,對事務有強有力的支持)

HDFS細節(jié)說明

Block塊概念

數(shù)據塊(block)是HDFS為文件提供的切塊后的數(shù)據儲存的空間,其默認大小為128M,若文件小于128M,則塊的大小為文件大小

NameNode

A.NameNode維護著HDFS中的元信息,數(shù)據格式參照如下:

/test/a.log(哪個文件),3(備份了幾份),{b1,b2}(切了幾塊),[{b1:[h0,h1,h3]}(第一塊的位置都在哪些datanode上),{b2:[h0,h2,h4]}(第二塊的位置都在哪些datanode上)

B.NameNode中的元數(shù)據(metadata)信息存儲在:

內存/文件中,內存中為實時信息,文件中為數(shù)據鏡像作為持久化存儲使用。

這些文件包括:

fsimage :元數(shù)據鏡像文件。存儲某NameNode元數(shù)據信息,但并不是實時同步內存中的數(shù)據。

edits :記錄操作的日志文件

元數(shù)據具體儲存流程如下:

NameNode會首先將元數(shù)據寫到edits文件中,寫入成功后才會修改內存中的元數(shù)據,并向客戶端返回這才請求的結果。

此時不會更新fsimage中的元數(shù)據,所以,fsimage中的元數(shù)據并不是實時的元數(shù)據,只有在達到條件時再根據edits進行更新,更新過程需要SecondaryNameNode(相當于NN的助理,專門負責合并的工作)參與。


上圖所示:達到條件后 snn會將nn中的fsimage和edits文件拷貝過來,同時nn中會創(chuàng)建一個新的edits.new文件,新的讀寫請求會寫入到這個edits.new中,在snn中將拷貝過來的fsimage和edits合并為一個新的fsimage,最后snn將合并完成的fsimage文件拷貝回nn中替換之前的fsimage,nn再將edtis.new改為edits

問:ssn可以對元數(shù)據做一定程度的備份,但不是熱備,對不對?那什么情況下可能造成NameNode元數(shù)據信息丟失?

snn并不是nn的熱備,但是能保存大部分備份數(shù)據。原因就在于在合并過程中,如果nn發(fā)生故障,那edits.new中的數(shù)據丟失了就找不回來了,找回來的只能是用于合并的原edits,因此通常NameNode和SNN要放置到不同機器中以此提升性能,并提供一定的元數(shù)據安全性。

何時觸發(fā)數(shù)據合并?:

根據配置文件設置的時間間隔:fs.checkpoint.period 默認3600秒;

根據配置文件設置的edits log大小 fs.checkpoint.size 默認64MB;

DataNode

DataNode節(jié)點會不斷向NameNode節(jié)點發(fā)送心跳報告。

初始化時,每個數(shù)據節(jié)點將當前存儲的數(shù)據塊告知NameNode節(jié)點。

通過向NameNode主動發(fā)送心跳保持與其聯(lián)系(3秒一次)

后續(xù)DataNode節(jié)點在工作的過程中,數(shù)據節(jié)點仍會不斷的更新NameNode節(jié)點與之對應的元數(shù)據信息,并接受來自NameNode節(jié)點的指令,創(chuàng)建、移動或者刪除本地磁盤上的數(shù)據塊。

如果10分鐘都沒收到dn的心跳,則認為其已經lost,并copy其上的block到其他dn

Block三個副本放置策略:

第一個副本:如果上傳文件的服務器本身就是DataNode,就放置在上傳文件的DN;如果是外部客戶端向集群上傳,就隨機選擇一臺磁盤不太滿,cpu不太忙的節(jié)點

第二個副本:放置在第一個副本不同機架的節(jié)點上(會自動找到不同機架)

第三個副本:放置在與第二個副本相同機架的節(jié)點上(機架內通訊比機架間通訊塊)

HDFS執(zhí)行流程

HDFS讀流程圖

1.OpenFile:使用HDFS提供的客戶端開發(fā)庫Client,向遠程的Namenode發(fā)起RPC請求;

Get Block: Namenode會視情況返回文件的部分或者全部block列表,對于每個block,Namenode都會返回有該block拷貝的三個三個DataNode地址;

客戶端開發(fā)庫Client會選取離客戶端最接近的DataNode來讀取block;如果客戶端本身就是DataNode,那么將從本地直接獲取數(shù)據.

讀取完當前block的數(shù)據后,關閉與當前的DataNode連接,并為讀取下一個block尋找最佳的DataNode,每讀取完一個block都會進行checksum驗證,如果讀取datanode時出現(xiàn)錯誤,客戶端會通知Namenode,然后再從下一個擁有該block拷貝的datanode繼續(xù)。當讀完列表的block后,且文件讀取還沒有結束,客戶端開發(fā)庫會繼續(xù)向Namenode獲取下一批的block列表。

當文件最后一個塊也都讀取完成后,datanode會連接namenode告知關閉文件。

HDFS寫流程(存入文件)

1.使用HDFS提供的客戶端開發(fā)庫Client,向遠程的Namenode發(fā)起RPC請求;Namenode會檢查要創(chuàng)建的文件是否已經存在,創(chuàng)建者是否有權限進行操作,成功則會為文件創(chuàng)建一個記錄(賬本(edits)上記錄一條),否則會讓客戶端拋出異常;

當客戶端開始寫入文件的時候,開發(fā)庫Client會將文件切分成多個packets(數(shù)據包,每個包是64k)(注意:文件會被切成數(shù)據包而非數(shù)據塊,數(shù)據塊是HDFS所提供的儲存這些數(shù)據包的空間),并在內部以數(shù)據隊列"data queue"的形式管理這些packets,并向Namenode申請新的blocks。

開發(fā)庫把packet以流的方式寫入第一個datanode中的block,該block把該packet存儲之后,再將其傳遞給的下一個datanode中的block,直到最后一個datanode中的block,這種寫數(shù)據的方式呈流水線的形式。

最后一個datanode成功存儲之后會返回一個信息,向上傳遞至客戶端,在客戶端的開發(fā)庫成功收到信息后,會從數(shù)據隊列中移除相應的packet。

6當所有的塊都存放完后,通知NameNode關閉文件。

HDFS的刪除流程

當NameNode執(zhí)行delete方法時,它只標記操作涉及的需要被刪除的數(shù)據塊,而不會主動聯(lián)系這些數(shù)據塊所在的DataNode節(jié)點。

當保存著這些數(shù)據塊的DataNode節(jié)點向NameNode節(jié)點發(fā)送心跳時,在心跳應答里,NameNode節(jié)點會向DataNode發(fā)出指令,從而把數(shù)據刪除掉。

所以在執(zhí)行完delete方法后的一段時間內,數(shù)據塊才能被真正的刪除掉。

安全模式

何時會進入安全模式,此時的特點:

在啟動HDFS后,會立即進入安全模式,此時不能操作hdfs中的文件,只能查看目錄文件名等,讀寫操作都不能進行。

為什么要進入安全模式?安全模式中所進行的任務:

namenode啟動時,需要載入fsimage文件到內存,同時執(zhí)行edits文件中各項操作

一旦在內存中成功建立文件系統(tǒng)元數(shù)據的映射,則創(chuàng)建一個新的fsimage文件(這個步驟不需要SNN的參與)和一個空的編輯文件。

在此階段NameNode收集各個DataNode的報告,當每個數(shù)據塊都備份了三份以上時(不足的會自動復制到三份以上),再經過若干時間,安全模式結束

補充:數(shù)據塊的具體存放位置是由誰來維護的?

Namenode只是告訴哪個datanode中要放哪些數(shù)據塊,而具體放在datanode中的哪個位置,由datanode決定(其內部維護了一個數(shù)據塊列表)

MapReduce

概述

MapReduce是一個分布式計算框架(HDFS是一個分布式文件儲存系統(tǒng)),解決了海量數(shù)據的計算問題。

MapReduce框架的節(jié)點組成結構

1、 ResourceManager工作職能:

A.知道管理哪些機器,即管理哪些NodeManager。

B.要有檢測機制,能夠檢測到NodeManager的狀態(tài)變換,通過RPC心跳來實現(xiàn)。

C.任務的分配和調度

2、 NodeManager工作職能:

A.能夠收到ResourceManager發(fā)過來的任務,并進行任務的處理。這里處理任務指的是Map任務或Reduce任務。

Map、Reduce的執(zhí)行步驟

要能說清整個流程

Map和reduce個數(shù)的確定:

要處理的文件被分成了幾塊儲存在HDFS上,當使用MR進行文件處理時,就會調用幾個map任務來處理這幾塊的數(shù)據;對map的輸出進行洗牌(shuffle),設置分區(qū)個數(shù)(相同key肯定在同一個區(qū),但是不同的key也可能在同一個區(qū))后,分了幾個區(qū)就會創(chuàng)建幾個reduce來處理這些分區(qū)中的數(shù)據

Mapreduce執(zhí)行溢寫原理

1.Mapper

每個MapperTask有一個環(huán)形內存緩沖區(qū),用于存儲map任務的輸出。默認大小100MB,一旦達到閥值0.8,一個后臺線程把內容寫到磁盤的指定目錄下的新建的一個溢出寫文件。

寫磁盤前,要partition,sort,Combiner(這些就是在shuffle中要做的事情)。如果有后續(xù)的數(shù)據,將會繼續(xù)寫入環(huán)形緩沖區(qū)中,最終寫入下一個溢出文件中。

等最后記錄寫完,合并全部溢出寫文件為一個分區(qū)且排序的文件。

2.Reducer

Reducer通過Http方式得到輸出文件的分區(qū)。

NodeManager為分區(qū)文件運行Reduce任務。復制階段把Map輸出復制到Reducer的內存或磁盤(一但Map任務完成,Reduce就開始復制map輸出)

小文件處理

小文件的定義

小文件指的是:

那些size比HDFS?的block?size(128M)小的多的文件。如果在HDFS中存儲海量的小文件,會產生很多問題。


大量小文件在HDFS中的問題

問題:太占namenode內存空間(1000個1M比1個1000M多占1000倍的namenode空間)

因此HDFS并不是為了有效的處理大量小文件而存在的。它主要是為了流式的訪問大文件而設計的。


大量小文件在mapreduce中的問題

Map?tasks通常是每次處理一個block的input,如果有大量小文件,就會有很多block,就會產生大量的map?tasks。Hadoop里每個task任務(map任務或reduce任務)的執(zhí)行都會啟動JVM來運行。啟動一個新的JVM將耗時1秒左右,對于運行時間較長(比如1分鐘以上)的job影響不大,但如果都是時間很短的task,那么頻繁啟停JVM會有開銷。


解決方法:

1.可以在一個JVM中允許task?reuse,以支持在一個JVM中運行多個map?task,以此來減少一些JVM的啟動消耗(通過設置mapred.job.reuse.jvm.num.tasks屬性,默認為1,-1為無限制)。

2.將多個小文件合成一個spilt,即用一個map任務來處理。

flume

1.flume概述

1.1.flume概念(日志收集)

1.1.1.flume概念

flume是分布式的,可靠的,高可用的,用于對不同來源的大量的日志數(shù)據進行有效收集、聚集和移動,并以集中式的數(shù)據存儲的系統(tǒng)。

運行原理:

將日志信息的每一行,包裝成json格式,再傳入flume系統(tǒng)中進行管理

補:json格式:

{“header”:{“name”:“l(fā)iming”,“age”:“18”},body:{日志中每一行字符串}}

2.flume中的概念、模型和特點

2.1.flume中的一些重要概念

2.1.1.flume Event:

flume 事件,被定義為一個具有有效荷載的字節(jié)數(shù)據流和可選的字符串屬性集。

2.1.2.flume Agent:

flume 代理,是一個進程承載從外部源事件流到下一個目的地的過程。包含source channel 和 sink。

2.1.3.Source

數(shù)據源,消耗外部傳遞給他的事件,外部源將數(shù)據按照flume Source 能識別的格式將Flume 事件發(fā)送給flume Source。

2.1.4.Channel

數(shù)據通道,是一個被動的存儲,用來保持事件,直到由一個flume Sink消耗。

2.1.5.Sink

數(shù)據匯聚點,代表外部數(shù)據存放位置。發(fā)送flume event到指定的外部目標。

各種不同Source(提供了不同類型的數(shù)據源,只需要在配置文件中更改source的配置)

1.Avro source:(用的最多)

監(jiān)聽Avro客戶端,將監(jiān)聽到的事件流作為數(shù)據源

3.Spooling Directory Source:

監(jiān)聽自動收集目錄,如果有文件傳入,flume就會以日志的形式解析,并收集這些文件,這些傳入的文件就相當于數(shù)據源

4.Netcat Source:

監(jiān)聽指定端口,并將接受到的數(shù)據每一行轉換為一個事件作為數(shù)據源

各種不同的Sink(是指收到的外部數(shù)據存放位置,只需要在配置文件中更改sink的配置):

2.File Roll Sink:

每隔指定時長,生成文件,保存這段時間收到的外部數(shù)據

3.Avro Sink:

作為多級流動、扇入、扇出的數(shù)據儲存中介使用,是多級流動、扇入、扇出的基礎

4.HDFS Sink:

將收到的外部數(shù)據儲存到HDFS中

各種不同的channel(是指收到的外部數(shù)據在中間過渡時的存放位置,只需要在配置文件中更改channel的配置):

1.Memory channel:

數(shù)據被臨時保存在內存中的指定大小的隊列

3.File Channel:

數(shù)據被持久保存在硬盤中

4.Spillable Memory Channel:

數(shù)據被保存在內存隊列和硬盤中(相當于1和3的結合,最常用)

Selector:

選擇器,用于扇出的兩種模式下,指定數(shù)據發(fā)送給哪個channel

Interceptors:

攔截器,在數(shù)據發(fā)送給channel前,對數(shù)據進行修改或刪除

1.Timestamp Interceptors:

修改數(shù)據,在event頭中加入當前處理時間(即:系統(tǒng)時間)

6.Search and Replace Interceptor:

先檢查event的body中數(shù)據,再替換(撿查和替換都是基于字符串的正則表達式的)

Processor:

用于實現(xiàn)負載均衡和失敗恢復的工具

1.Default Sink Processor:

這是默認情況下的使用,即:一個sink對應一個channel

2.Failover Sink Processor:

這是用于失敗恢復的情況(如:3個sink對應一個channel,分別設置優(yōu)先級,優(yōu)先級高的優(yōu)先使用,如果高的壞了,第二高的可以頂替他繼續(xù)工作,注:同時只能有一臺在工作,其余相當于備用的)

3.Load Balancing? Sink Processor:

這是用于負載均衡的情況(有輪詢或隨機兩種方式)

hive

分區(qū)表的含義:

hive支持分區(qū)表,當文件中數(shù)據量很大,且常常出現(xiàn)按某一字段查詢(如:...where country=’...’,這就叫按country字段進行查詢),需要對數(shù)據進行分區(qū),因為這樣可以極大的提高查詢時的效率?。。。]分區(qū)時需要在所有數(shù)據中先提取符合該字段的數(shù)據,分區(qū)以后可以直接在這個country分區(qū)中查詢了!)

六、HIVE語法(相當于數(shù)據庫的sql)

9.補充:

增加分區(qū):

ALTER TABLE book add? PARTITION (category = 'zazhi') location '/user/hive/warehouse/datax.db/book/category=zazhi';

刪除分區(qū):

ALTER TABLE table_name DROP partition_spec, partition_spec,...

重命名表:

ALTER TABLE table_name RENAME TO new_table_name

修改列名:

ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]

增加/替換列:

ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)

查看表名,部分匹配

SHOW TABLES 'page.*';

SHOW TABLES '.*view';

查看某表的所有Partition,如果沒有就報錯:

SHOW PARTITIONS page_view;

查看某表結構:

DESCRIBE invites;

查看分區(qū)內容

SELECT a.foo FROM invites a WHERE a.ds='2008-08-15';

Hbase(了解)

一、HBASE概述

HBASE與HIVE的區(qū)別:

HBASE:是基于hadoop的數(shù)據庫工具(把hadoop作為數(shù)據庫)

HIVE:是基于hadoop的數(shù)據倉庫工具(把hadoop作為數(shù)據倉庫)

優(yōu)點:

1.是一種 NoSQL 非關系型的數(shù)據庫 不符合關系型數(shù)據庫的范式 (關系型數(shù)據庫的范式:可以將數(shù)據放在行列構成的很多表中,進行表的關聯(lián),從而闡明數(shù)據之間的關系)

2.適合存儲:半結構化(如:json格式)與非結構化的數(shù)據(不能用行列表示的數(shù)據,如圖片,視頻,音頻;語音識別技術、圖像識別技術、機械學習技術實際上就是在處理非結構化數(shù)據)

3.適合存儲稀疏的數(shù)據,hbase中空的數(shù)據不占用空間(mysql中空數(shù)據也占用空間)

為什么會出現(xiàn)空數(shù)據呢?

因為是非結構化的數(shù)據,因此勢必有些格中沒有數(shù)據

4.面向列(族)進行存儲,方便對數(shù)據進行壓縮 (mysql是以行進行存儲)

5.提供實時增刪改查的能力 是一種真正的數(shù)據庫

6.可以存儲海量數(shù)據,性能也很強大,可以實現(xiàn)上億條記錄的毫秒級別的查詢

7.是一個高可靠性,高性能,面向列,可伸縮的分布式存儲系統(tǒng) 利用hbase技術可以在廉價的PC上搭建起大規(guī)模結構化存儲集群。

8.HBase利用HadoopHDFS作為其文件存儲系統(tǒng),利用Hadoop的MapReduce來處理HBase中的海量數(shù)據,利用Zookeeper作為協(xié)調工具

缺點:

不能提供嚴格的事務控制,只能在行級別保證事務

(2)邏輯結構

hbase通過表來存儲數(shù)據 但是表的結構和關系型數(shù)據庫非常的不一樣

行鍵(RowKey ):

列族(Column Family ):

列(Column):

單元格與時間戳(cell timestamp )

kafka

一、Kafka概述

分布式消息隊列

按topic分類開放數(shù)據

Producer Consumer

Broker

使用zookeeper做為集群的協(xié)調工具

kafka的特點:

高吞吐量

Kafka 每秒可以生產約50 MB消息,每秒處理 110 MB消息

持久化數(shù)據存儲

將消息持久化到磁盤

分布式系統(tǒng)易于擴展

所有的 producer、broker 和 consumer 都會有多個,均為分布式的。無需停機即可擴展機器。

客戶端consumer自己維護自己的狀態(tài)(狀態(tài)即:該讀取什么數(shù)據了)

消息被處理的狀態(tài)是在 consumer 端維護,而不是由 server 端維護。當失敗時能自動平衡。

二、Kafka中的基本概念

Kafka將消息以topic為單位進行歸納。

將向Kafka topic發(fā)布消息的程序稱為producers.

將預訂topics并消費消息的程序稱為consumer.

Kafka以集群的方式運行,可以由一個或多個服務組成,每個服務叫做一個broker.

producers通過網絡將消息發(fā)送到Kafka集群,集群向消費者提供消息。

客戶端和服務端通過TCP協(xié)議通信。Kafka提供了Java客戶端,并且對多種語言都提供了支持。

三、Topics、Producers、Consumers

1.Topics(主題)

1.一個topic是對一組消息的歸納。(topic的作用:可以讓多個不同的團隊使用同一個kafka,而不會發(fā)生數(shù)據混亂)

2.對每個topic,Kafka 對它的日志進行了分區(qū)(類似于圖書館的圖書分類擺放,實現(xiàn)了負載均衡,并且每個分區(qū)在Kafka集群的若干服務器中都有副本,這樣這些持有副本的服務可以共同處理數(shù)據和請求(類似于leader和flower的關系),副本數(shù)量是可以配置的。)

3.分區(qū)中的每個消息都有一個連續(xù)的序列號叫做offset,用來在分區(qū)中唯一的標識這個消息。

4.在一個可配置的時間段內,Kafka集群保留所有發(fā)布的消息,不管這些消息有沒有被消費。比如,如果消息的保存策略被設置為2天,那么在一個消息被發(fā)布的兩天時間內,它都是可以被消費的,不論其是否被消費過,之后它將被丟棄以釋放空間。

5.kafa的最大優(yōu)點:

Kafka的性能是和數(shù)據量無關的常量級的,所以保留太多的數(shù)據并不是問題。(其性能由硬件決定)

6.將日志分區(qū)的目的:

首先這使得每個日志的數(shù)量不會太大,可以在單個服務上保存。另外每個分區(qū)可以單獨發(fā)布和消費,為并發(fā)操作topic提供了一種可能。并且在kafka中通過分區(qū)實現(xiàn)了負載均衡和失敗恢復

2.Producers

Producer將消息發(fā)布到它指定的topic中,并負責決定發(fā)布到哪個分區(qū)。通常簡單的由負載均衡機制隨機選擇分區(qū),但也可以通過特定的分區(qū)函數(shù)選擇分區(qū)。

3.Consumers

**實際上每個consumer唯一需要維護的數(shù)據是消息在日志中的位置,也就是offset.這個offset由consumer來維護

**消費消息通常有兩種模式:隊列模式(queuing)和發(fā)布-訂閱模式(publish-subscribe)。

(1)隊列模式

隊列模式中,多個consumers可以同時從服務端讀取消息,每個消息只被其中一個consumer讀到;

(2)發(fā)布訂閱模式

發(fā)布-訂閱模式中消息被廣播到所有的consumer中。

如果所有的consumer都在一個組中,這就成為了傳統(tǒng)的隊列模式,在各consumer中實現(xiàn)負載均衡。

如果所有的consumer都不在不同的組中,這就成為了發(fā)布-訂閱模式,所有的消息都被分發(fā)到所有的consumer中。

為什么大數(shù)據環(huán)境下的消息隊列常選擇kafka?

分布式存儲數(shù)據,提供了更好的性能 可靠性 可擴展能力,且按照主題、分區(qū)來分布式存放數(shù)據,持久化存儲,提供海量數(shù)據存儲能力,性能和磁盤的性能相關和數(shù)據量的大小無關

storm

一、Storm作用:

分布式實時計算系統(tǒng)

二、Storm組件

1.結構

storm結構稱為topology(拓撲),由stream(數(shù)據流),spout(噴嘴-數(shù)據流的生成者),bolt(閥門-數(shù)據流運算者)組成(參考圖:Storm組成結構)。

不同于Hadoop中的job,Storm中的topology會一直運行下去,除非進程被殺死或取消部署。

2.Stream

Storm的核心數(shù)據結構是tuple(元組),本質上是包含了一個或多個鍵值對的列表。Stream是由無限制的tuple組成的序列。

3.spout

spout連接到數(shù)據源,將數(shù)據轉化為一個個的tuple,并將tuple作為數(shù)據流進行發(fā)射,通常不會用于處理業(yè)務邏輯,從而可以很方便的實現(xiàn)spout的復用。開發(fā)一個spout的主要工作就是利用API編寫代碼從數(shù)據源消費數(shù)據流。

spout的數(shù)據源可以有很多種來源:

Kafka

Hbase

Mysql

Redis

hdfs

4.bolt(真正進行業(yè)務處理的部分)

bolt主要負責數(shù)據的運算,將接收到的數(shù)據實施運算后,選擇性的輸出一個或多個數(shù)據流。

一個bolt可以接收多個由spout或其他bolt發(fā)射的數(shù)據流,從而可以組建出復雜的數(shù)據轉換和處理的網絡拓撲結構。

bolt常見的典型功能:

過濾

連接和聚合

計算

數(shù)據庫的讀寫

Hadoop常見參數(shù)控制+調優(yōu)策略

配置所在文件 參數(shù) 參數(shù)默認值 作用

hdfs-site.xml dfs.heartbeat.interval 3 DN的心跳間隔,秒

在集群網絡通信狀態(tài)不好的時候,適當調大

hdfs-site.xml dfs.blocksize 134217728 塊大小,默認是128MB

必須得是1024的整數(shù)倍

mapred-site.xml mapreduce.task.io.sort.mb 100 任務內部排序緩沖區(qū)大小,默認是100MB

此參數(shù)調大,能夠減少Spil溢寫次數(shù),減少磁盤I/O

mapred-site.xml mapreduce.reduce.shuffle.parallelcopies 5 Reduce Task 啟動的并發(fā)拷貝數(shù)據的線程數(shù)

建議,盡可能等于或接近于Map任務數(shù)量,

但是不易過多。

mapred-site.xml io.sort.factor 10 merge文件合并因子,如果結果文件數(shù)量太多,可以適當調大,從而減少I/O次數(shù)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容