2023 Hive 面試寶典

先說(shuō)一些廢話

總結(jié)一下Hive面試寶典,方便讀者快速過(guò)一遍Hive面試所需要的知識(shí)點(diǎn)

Hive的介紹

Hive和Hadoop的關(guān)系

  1. Hive利用hdfs存儲(chǔ)數(shù)據(jù),利用MapReduce查詢數(shù)據(jù)
  2. Hive的數(shù)據(jù)存儲(chǔ)在hdfs上,簡(jiǎn)單的說(shuō)Hive就是hdfs的簡(jiǎn)單一種映射,比如:Hive的一張表映射hdfs上的一個(gè)文件,Hive的一個(gè)數(shù)據(jù)庫(kù)就映射為hdfs上的文件夾
  3. Hive是一個(gè)計(jì)算框架,他是MapReduce的一種封裝,實(shí)際上他的底層還是MR,Hive就是用人們熟悉的sql對(duì)數(shù)據(jù)進(jìn)行分析的
  4. Hive執(zhí)行程序是運(yùn)行在Yarn上的

Hive的特點(diǎn)

  1. Hive可以自由的擴(kuò)展集群的規(guī)模,一般情況下不需要重啟服務(wù)(世界上最大的Hadoop集群在Yahoo!,2009年的規(guī)模在4000臺(tái)節(jié)點(diǎn)左右)
  2. Hive支持用戶自定義函數(shù),用戶可以根據(jù)自己的需求來(lái)實(shí)現(xiàn)自己的函數(shù)(可能會(huì)引申自定義函數(shù))
  3. 良好的容錯(cuò)性,節(jié)點(diǎn)出現(xiàn)問(wèn)題SQL仍可完成執(zhí)行(可能會(huì)拓展數(shù)據(jù)傾斜相關(guān)問(wèn)題,或者直接問(wèn)你你工作中有沒(méi)有遇到這樣的問(wèn)題)

Hive的缺點(diǎn)

  1. Hive的HQL表達(dá)能力有限。迭代式算法無(wú)法表達(dá);數(shù)據(jù)挖掘方面不擅長(zhǎng)
  2. Hive的效率比較低。Hive自動(dòng)生成的MapReduce作業(yè),通常情況下不夠智能化;Hive調(diào)優(yōu)比較困難,粒度較粗
  3. Hive執(zhí)行延遲
    • Hive 在查詢數(shù)據(jù)的時(shí)候,由于沒(méi)有索引,需要掃描整個(gè)表,因此延遲較高
    • 另外一個(gè)導(dǎo)致 Hive 執(zhí)行延遲高的因素是 MapReduce框架,由于MapReduce 本身具有較高的延遲,因此在利用MapReduce 執(zhí)行Hive查詢時(shí),也會(huì)有較高的延遲
    • 相對(duì)的,數(shù)據(jù)庫(kù)的執(zhí)行延遲較低。當(dāng)然,這個(gè)低是有條件的,即數(shù)據(jù)規(guī)模較小,當(dāng)數(shù)據(jù)規(guī)模大到超過(guò)數(shù)據(jù)庫(kù)的處理能力的時(shí)候,Hive的并行計(jì)算顯然能體現(xiàn)出優(yōu)勢(shì)

Hive常見的應(yīng)用場(chǎng)景

  1. 日志分析:大部分互聯(lián)網(wǎng)公司使用Hive進(jìn)行日志分析,包括百度、淘寶等
    • 統(tǒng)計(jì)網(wǎng)站一個(gè)時(shí)間段內(nèi)的pv、uv
    • 多維度數(shù)據(jù)分析
  2. 海量結(jié)構(gòu)化數(shù)據(jù)離線分析

Hive和mysql的區(qū)別

  1. Hive采用了類SQL的查詢語(yǔ)言HQL(hive query language),除了HQL之外,其余無(wú)任何相似的地方,Hive是為了數(shù)據(jù)倉(cāng)庫(kù)設(shè)計(jì)的
  2. 存儲(chǔ)位置:Hive在Hadoop上;mysql將數(shù)據(jù)存儲(chǔ)在設(shè)備或本地系統(tǒng)中
  3. 數(shù)據(jù)更新:Hive不支持?jǐn)?shù)據(jù)的改寫和添加,是在加載的時(shí)候就已經(jīng)確定好了;數(shù)據(jù)庫(kù)可以CRUD
  4. 索引:Hive無(wú)索引,每次掃描所有數(shù)據(jù),底層是MR,并行計(jì)算,適用于大數(shù)據(jù)量;mysql有索引,適合在線查詢數(shù)據(jù)
  5. 執(zhí)行:Hive底層是MarReduce;mysql底層是執(zhí)行引擎
  6. 可擴(kuò)展性:Hive:大數(shù)據(jù)量,慢慢擴(kuò)去吧;mysql:相對(duì)就很少了

Hive的架構(gòu)

# Hive架構(gòu)簡(jiǎn)易示意

Meta Store -> 
Client (CLI/JDBC/WebGUI + 
        Driver/驅(qū)動(dòng) + 
        SQL Parser/解析器 + 
        Physical Plan/編譯器 + 
        QueryOptimizer/優(yōu)化器 + 
        Execution/執(zhí)行器) ->
MapReduce ->
HDFS
  1. 用戶接口:Hive 對(duì)外提供了三種服務(wù)模式,即 Hive 命令行模式(CLI),Hive 的 Web 模式(WUI),Hive 的遠(yuǎn)程服務(wù)(Client)
    • 其中最常用的是 CLI shell 命令行,CLI 啟動(dòng)的時(shí)候,會(huì)同時(shí)啟動(dòng)一個(gè)Hive副本
    • WUI 是通過(guò)瀏覽器訪問(wèn) Hive,默認(rèn)端口是9999
    • Client 是Hive的客戶端,,在啟動(dòng) Client模式 的時(shí)候,需要指出 Hive Server 所在節(jié)點(diǎn),并且在該節(jié)點(diǎn)啟動(dòng) Hive Server
    • JDBC/ODBC用 JAVA 實(shí)現(xiàn),與傳統(tǒng)數(shù)據(jù)庫(kù) JDBC 類似
  2. 元數(shù)據(jù)存儲(chǔ):通常是存儲(chǔ)在關(guān)系數(shù)據(jù)庫(kù)如 mysql , derby中
    • Hive中的元數(shù)據(jù)包括表的名字,表的列和分區(qū)及其屬性,表的屬性(是否為外部表等),表的數(shù)據(jù)所在目錄等
  3. 解釋器、編譯器、優(yōu)化器、執(zhí)行器
    • 解釋器、編譯器、優(yōu)化器完成 HQL 查詢語(yǔ)句從詞法分析、語(yǔ)法分析、編譯、優(yōu)化以及查詢計(jì)劃的生成
    • 生成的查詢計(jì)劃存儲(chǔ)在 HDFS 中,并在隨后有 MapReduce 調(diào)用執(zhí)行(注意?。?!包含*的查詢,比如select * from tbl不會(huì)生成MapRedcue任務(wù))
    • ===============================================================
    • 解析器(parser):將查詢字符串轉(zhuǎn)化為解析樹表達(dá)式
    • ===============================================================
    • 編譯器(physical plan):分為語(yǔ)義分析器(semantic analyzer)邏輯策略生成器(logical plan generator)
    • 語(yǔ)義分析器(semantic analyzer):將解析樹表達(dá)式轉(zhuǎn)換為基于塊(block-based)的內(nèi)部查詢表達(dá)式
    • 邏輯策略生成器(logical plan generator):將內(nèi)部查詢表達(dá)式轉(zhuǎn)換為邏輯策略,這些策略由邏輯操作樹組成
    • ===============================================================
    • 優(yōu)化器(optimizer):通過(guò)邏輯策略構(gòu)造多途徑并以不同方式重寫

Hive的數(shù)據(jù)

Hive的數(shù)據(jù)模型

  1. Hive中所有的數(shù)據(jù)都存儲(chǔ)在hdfs中,沒(méi)有專門的數(shù)據(jù)存儲(chǔ)格式(可支持TextFile,SequenceFile,ParquetFile,RCFILE等)
  2. 只需要在創(chuàng)建表的時(shí)候告訴Hive數(shù)據(jù)中的列分隔符和行分隔符,Hive就可以解析數(shù)據(jù)
  3. Hive中包含以下數(shù)據(jù)模型:DB、Table、External Table、Partition、Bucket
    • DB:在hdfs中表現(xiàn)為${hive.metastore.warehouse.dir}目錄下一個(gè)文件夾
    • Table:在hdfs中表現(xiàn)所屬db目錄下一個(gè)文件夾,普通表刪除表后,hdfs上的文件都刪了
    • External Table:外部表, 與table類似,不過(guò)其數(shù)據(jù)存放位置可以在任意指定路徑,外部表刪除后,hdfs上的文件沒(méi)有刪除,只是把文件刪除了
    • Partition:在hdfs中表現(xiàn)為table目錄下的子目錄
    • Bucket:桶在hdfs中表現(xiàn)為同一個(gè)表目錄下根據(jù)hash散列之后的多個(gè)文件,會(huì)根據(jù)不同的文件把數(shù)據(jù)放到不同的文件中

Hive的底層如何存儲(chǔ)Null值

  1. Null在Hive底層默認(rèn)是用'\N'來(lái)存儲(chǔ)的
  2. 能夠經(jīng)過(guò)alter table test SET SERDEPROPERTIES('serialization.null.format' = 'a');來(lái)修改

Hive中元數(shù)據(jù)metadata和元數(shù)據(jù)商店metastore的作用

  1. metadata即元數(shù)據(jù),元數(shù)據(jù)包含用Hive創(chuàng)建的database、tabel等的元信息,元數(shù)據(jù)存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)(RDBMS)中,如derby、mysql等
  2. metastore的作用是:客戶端連接metastore服務(wù),metastore再去連接mysql數(shù)據(jù)庫(kù)來(lái)存取元數(shù)據(jù),
    有了metastore服務(wù),就可以有多個(gè)客戶端同時(shí)連接,而且這些客戶端不需要知道m(xù)ysql數(shù)據(jù)庫(kù)的用戶名和密碼,只需要連接metastore服務(wù)即可

Hive有哪些保存元數(shù)據(jù)metadata的方式

  1. 內(nèi)嵌模式:將元數(shù)據(jù)保存在本地內(nèi)嵌的derby數(shù)據(jù)庫(kù)中,內(nèi)嵌的derby數(shù)據(jù)庫(kù)每次只能訪問(wèn)一個(gè)數(shù)據(jù)文件,也就意味著它不支持多會(huì)話連接,適用于用來(lái)實(shí)驗(yàn),不適用于生產(chǎn)環(huán)境
  2. 本地模式:將元數(shù)據(jù)保存在本地獨(dú)立的數(shù)據(jù)庫(kù)中(一般是mysql),這可以支持多會(huì)話連接
  3. 遠(yuǎn)程模式:把元數(shù)據(jù)保存在遠(yuǎn)程獨(dú)立的mysql數(shù)據(jù)庫(kù)中,避免每個(gè)客戶端都去安裝mysql數(shù)據(jù)庫(kù)
  4. 需要注意的是: 內(nèi)存數(shù)據(jù)庫(kù)derby,安裝小,但是數(shù)據(jù)存在內(nèi)存,不穩(wěn)定。mysql數(shù)據(jù)庫(kù),數(shù)據(jù)存儲(chǔ)模式可以自己設(shè)置,持久化好,查看方便

Hive元數(shù)據(jù)存儲(chǔ)方式中,本地模式和遠(yuǎn)程模式的區(qū)別

  1. 本地元存儲(chǔ)遠(yuǎn)程元存儲(chǔ)都采用外部數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)元數(shù)據(jù)
  2. 本地元存儲(chǔ)不需要單獨(dú)起metastore服務(wù),用的是跟Hive在同一個(gè)進(jìn)程里的metastore服務(wù)
  3. 遠(yuǎn)程元存儲(chǔ)需要單獨(dú)起metastore服務(wù),然后每個(gè)客戶端都在配置文件里配置連接到該metastore服務(wù),遠(yuǎn)程元存儲(chǔ)的metastore服務(wù)和Hive運(yùn)行在不同的進(jìn)程

Hive的數(shù)據(jù)類型

  1. 基本數(shù)據(jù)類型,因?yàn)镠ive的底層是用java開發(fā),所以基本數(shù)據(jù)類型和java保持一致
    • 整型 tinyint(字節(jié)整型) / smallint(短整型) / int(整型) / bigint(長(zhǎng)整型),分別占用1/2/4/8個(gè)字節(jié),等價(jià)于java的 byte/short/int/long
    • 浮點(diǎn)型 float(浮點(diǎn)型) / double(雙精度浮點(diǎn)型),分別占用4/8個(gè)字節(jié),等價(jià)于java的 float/double
    • 字符型 string,等價(jià)于數(shù)據(jù)庫(kù)的 varchar,可變字符串,理論上可以存儲(chǔ)2GB的字節(jié)
    • 布爾型 boolean,等價(jià)于java的 boolean
  2. 復(fù)雜數(shù)據(jù)類型
    • array/map,等價(jià)于java的array/map
    • struct,等價(jià)于c語(yǔ)言中的struct
  3. 類型轉(zhuǎn)換
    • Hive 的原子數(shù)據(jù)類型是可以進(jìn)行隱式轉(zhuǎn)換的,類似于 Java 的類型轉(zhuǎn)換
    • 例如某表達(dá)式使用 int 類型,tinyint 會(huì)自動(dòng)轉(zhuǎn)換為 int 類型
    • 但是 Hive 不會(huì)進(jìn)行反向轉(zhuǎn)化,例如,某表達(dá)式使用 tinyint 類型,int 不會(huì)自動(dòng)轉(zhuǎn)換為 tinyint 類型,它會(huì)返回錯(cuò)誤,除非使用 CAST 操作
    • ===============================================================
    • 可以使用 CAST 操作顯示進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換
    • 例如 CAST('1' AS INT) 將把字符串'1' 轉(zhuǎn)換成整數(shù) 1
    • 如果強(qiáng)制類型轉(zhuǎn)換失敗,如執(zhí)行 CAST('X' AS INT),表達(dá)式返回空值 NULL

Hive的隱式類型轉(zhuǎn)換規(guī)則

  1. 任何整數(shù)類型都可以隱式地轉(zhuǎn)換為一個(gè)范圍更廣的類型,如 tinyint 可以轉(zhuǎn)換成 int,int 可以轉(zhuǎn)換成 bigint
  2. 所有整數(shù)類型、float 和 string 類型都可以隱式地轉(zhuǎn)換成 double
  3. tinyint、smallint、int 都可以轉(zhuǎn)換為 float
  4. boolean 類型不可以轉(zhuǎn)換為任何其它的類型

Hive數(shù)據(jù)存儲(chǔ)所使用的文件格式

  1. 默認(rèn)是TextFile文件格式
    • 文本格式,Hive的默認(rèn)格式,數(shù)據(jù)不壓縮,磁盤開銷大、數(shù)據(jù)解析開銷大
    • 對(duì)應(yīng)的Hive API為:org.apache.hadoop.mapred.TextInputFormat和org.apache.hive.ql.io.HiveIgnoreKeyTextOutputFormat;
    • 可結(jié)合Gzip、Bzip2使用(系統(tǒng)自動(dòng)檢查,執(zhí)行查詢時(shí)自動(dòng)解壓),但是使用這種方式,hive不會(huì)對(duì)數(shù)據(jù)進(jìn)行切分,從而無(wú)法對(duì)數(shù)據(jù)進(jìn)行并行操作
  2. RCFile文件格式
    • RCFile是一種行列存儲(chǔ)相結(jié)合的存儲(chǔ)方式,先將數(shù)據(jù)按行進(jìn)行分塊再按列式存儲(chǔ),保證同一條記錄在一個(gè)塊上,避免讀取多個(gè)塊,有利于數(shù)據(jù)壓縮和快速進(jìn)行列存儲(chǔ)
    • 對(duì)應(yīng)Hive API為:org.apache.hadoop.hive.ql.io.RCFileInputFormat和org.apache.hadoop.hive.ql.io.RCFileOutputFormat;
  3. ORCFile文件格式
    • 數(shù)據(jù)按行分塊,每塊按照列存儲(chǔ),不是真正意義上的列存儲(chǔ),可以理解為分段列存儲(chǔ)
    • 用于降低Hadoop數(shù)據(jù)存儲(chǔ)空間和加速Hive查詢速度
    • ORCfile特點(diǎn)是壓縮比比較高,壓縮快,快速列存取,是RCfile的改良版本,相比RCfile能夠更好的壓縮,更快的查詢
    • 需要注意的是ORC在讀寫時(shí)候需要消耗額外的CPU資源來(lái)壓縮和解壓縮,當(dāng)然這部分的CPU消耗是非常少的
    • 優(yōu)點(diǎn):
    每個(gè)task只輸出單個(gè)文件,減少namenode負(fù)載;
    支持各種復(fù)雜的數(shù)據(jù)類型,比如:datetime,decima以及復(fù)雜類型struct、list、map;
    文件中存儲(chǔ)了一些輕量級(jí)的索引數(shù)據(jù);
    基于數(shù)據(jù)類型的塊模式壓縮:integer類型的列用行程長(zhǎng)度編碼,string類型的列使用字典編碼;
    用多個(gè)相互獨(dú)立的recordReaders并行讀相同的文件
    無(wú)需掃描markers即可分割文件
    綁定讀寫所需內(nèi)存
    metadata存儲(chǔ)用protocol buffers,支持添加和刪除列
    
  4. SequenceFile文件格式
    • Hadoop提供的二進(jìn)制文件,Hadoop支持的標(biāo)準(zhǔn)文件
    • 數(shù)據(jù)直接序列化到文件中,SequenceFile文件不能直接查看,可以通過(guò)Hadoop fs -text查看
    • SequenceFile具有使用方便、可分割、可壓縮、可進(jìn)行切片,壓縮支持NONE、RECORD、BLOCK(優(yōu)先)
    • 對(duì)應(yīng)Hive API:org.apache.hadoop.mapred.SequenceFileInputFormat和org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat;
  5. Parquet文件格式
    • 二進(jìn)制存儲(chǔ),面向分析性的存儲(chǔ)格式
    • 能夠很好的壓縮,同時(shí)減少大量的表掃描和反序列化的時(shí)間,有很好的查詢性能,支持有限的模式演進(jìn),但是寫速度通常比較慢
    • Parquet文件是以二進(jìn)制方式存儲(chǔ)的,所以是不可以直接讀取的,文件中包括該文件的數(shù)據(jù)和元數(shù)據(jù),因此Parquet格式文件是自解析的
  6. 總結(jié)
    • TextFile 存儲(chǔ)空間消耗比較大,并且壓縮的text無(wú)法分割和合并查詢的效率最低,可以直接存儲(chǔ),加載數(shù)據(jù)的速度最高
    • SequenceFile 存儲(chǔ)空間消耗最大,壓縮的文件可以分割和合并,查詢效率高
    • ORCFile / RCFile 存儲(chǔ)空間最小,查詢的效率最高,加載的速度最低
    • Parquet 格式是列式存儲(chǔ),有很好的壓縮性能和表掃描功能
    • SequenceFile / ORCFile / RCFile 格式的表不能直接從本地文件導(dǎo)入數(shù)據(jù),數(shù)據(jù)要先導(dǎo)入到TextFile格式的表中,
      然后再?gòu)腡extFile表中導(dǎo)入到SequenceFile/ORCFile/RCFile表中

Hive中使用的壓縮算法

  1. 我們?cè)紨?shù)據(jù)使用的是LZO的壓縮格式,因?yàn)樵紨?shù)據(jù)比較大,所以選擇了支持切割的LZO壓縮
  2. 清洗過(guò)的數(shù)據(jù)存到DWD層,我們?cè)贒WS中需要對(duì)清洗后的數(shù)據(jù)進(jìn)行分析,所以我們DWD層使用的存儲(chǔ)格式是Parquet,壓縮格式是Snappy
  3. 之前我們壓縮還遇到過(guò)一個(gè)問(wèn)題,當(dāng)時(shí)之前的項(xiàng)目中使用的是Snappy+ORC存儲(chǔ),后來(lái)我發(fā)現(xiàn)使用Snappy+ORC 存儲(chǔ)比ORC單獨(dú)存儲(chǔ)還多占用了近一半的空間
  4. 后來(lái)我又對(duì)各個(gè)壓縮格式及存儲(chǔ)格式的結(jié)合做了一個(gè)測(cè)試,最終單獨(dú)使用ORC存儲(chǔ)節(jié)省了大量的空間
  5. Snappy壓縮格式
    • 其中壓縮比bzip2 > zlib > gzip > deflate > snappy > lzo > lz4,在不同的測(cè)試場(chǎng)景中,會(huì)有差異,這僅僅是一個(gè)大概的排名情況
    • bzip2、zlib、gzip、deflate可以保證最小的壓縮,但在運(yùn)算中過(guò)于消耗時(shí)間
    • 從壓縮性能上來(lái)看:lz4 > lzo > snappy > deflate > gzip > bzip2,其中l(wèi)z4、lzo、snappy壓縮和解壓縮速度快,壓縮比低
    • 所以一般在生產(chǎn)環(huán)境中,經(jīng)常會(huì)采用lz4、lzo、snappy壓縮,以保證運(yùn)算效率

什么是數(shù)據(jù)可分割

  1. 在考慮如何壓縮那些將由MapReduce處理的數(shù)據(jù)時(shí),考慮壓縮格式是否支持分割是很重要的。
    考慮存儲(chǔ)在HDFS中的未壓縮的文件,其大小為1GB,HDFS的塊大小為64MB,所以該文件將被存儲(chǔ)為16塊。
    將此文件用作輸入的MapReduce作業(yè)會(huì)創(chuàng)建1個(gè)輸人分片(split,也稱為“分塊”。對(duì)于block,我們統(tǒng)一稱為“塊”。)
    每個(gè)分片都被作為一個(gè)獨(dú)立map任務(wù)的輸入單獨(dú)進(jìn)行處理

  2. 現(xiàn)在假設(shè),該文件是一個(gè)gzip格式的壓縮文件,壓縮后的大小為1GB。和前面一樣,HDFS將此文件存儲(chǔ)為16塊。
    然而,針對(duì)每一塊創(chuàng)建一個(gè)分塊是沒(méi)有用的,因?yàn)椴豢赡軓膅zip數(shù)據(jù)流中的任意點(diǎn)開始讀取,map任務(wù)也不可能獨(dú)立于其他分塊只讀取一個(gè)分塊中的數(shù)據(jù)。
    gzip格式使用DEFLATE來(lái)存儲(chǔ)壓縮過(guò)的數(shù)據(jù),DEFLATE將數(shù)據(jù)作為一系列壓縮過(guò)的塊進(jìn)行存儲(chǔ)。
    問(wèn)題是,每塊的開始沒(méi)有指定用戶在數(shù)據(jù)流中任意點(diǎn)定位到下一個(gè)塊的起始位置,而是其自身與數(shù)據(jù)流同步。
    因此,gzip不支持分割(塊)機(jī)制。

  3. 在這種情況下,MapReduce不分割gzip格式的文件,因?yàn)樗垒斎胧莋zip壓縮格式的(通過(guò)文件擴(kuò)展名得知),而gzip壓縮機(jī)制不支持分割機(jī)制。
    因此一個(gè)map任務(wù)將處理16個(gè)HDFS塊,且大都不是map的本地?cái)?shù)據(jù)。
    與此同時(shí),因?yàn)閙ap任務(wù)少,所以作業(yè)分割的粒度不夠細(xì),從而導(dǎo)致運(yùn)行時(shí)間變長(zhǎng)。

關(guān)于壓縮模式說(shuō)明

  1. 壓縮模式評(píng)價(jià):
    可使用以下三種標(biāo)準(zhǔn)對(duì)壓縮方式進(jìn)行評(píng)價(jià):
    壓縮比:壓縮比越高,壓縮后文件越小,所以壓縮比越高越好。
    壓縮時(shí)間:越快越好。
    已經(jīng)壓縮的格式文件是否可以再分割:可以分割的格式允許單一文件由多個(gè)Mapper程序處理,可以更好的并行化。
    
  2. 壓縮模式對(duì)比
    BZip2有最高的壓縮比但也會(huì)帶來(lái)更高的CPU開銷,Gzip較BZip2次之。
    如果基于磁盤利用率和I/O考慮,這兩個(gè)壓縮算法都是比較有吸引力的算法。
    LZO和Snappy算法有更快的解壓縮速度,如果更關(guān)注壓縮、解壓速度,它們都是不錯(cuò)的選擇。 
    LZO和Snappy在壓縮數(shù)據(jù)上的速度大致相當(dāng),但Snappy算法在解壓速度上要較LZO更快。
    Hadoop的會(huì)將大文件分割成HDFS block(默認(rèn)64MB)大小的splits分片,每個(gè)分片對(duì)應(yīng)一個(gè)Mapper程序。
    在這幾個(gè)壓縮算法中 BZip2、LZO、Snappy壓縮是可分割的,Gzip則不支持分割。
    

Hive的安裝與使用

當(dāng)前版本請(qǐng)閱讀以下參考資料,后期再行完善

  1. hive的安裝和使用
  2. Hive入門及常用指令
  3. 更多進(jìn)階內(nèi)容請(qǐng)自行百度拓展查閱

如何在Hive中集成HBase

  1. 將Hbase的客戶端jar拷貝至Hive/lib目錄下
  2. 修改hive/conf下的hive-site.xml配置文件,添加如下屬性:
    <poperty>
        <name>hbase.zookeeper.quorum</name>
        <value>hadoop</value>
    </poperty>
    
  3. 啟動(dòng)Hive,創(chuàng)建表管理表hbase_table_1,指定數(shù)據(jù)存儲(chǔ)在Hbase表中,主要是通過(guò)stored by HBaseStorageHandler類來(lái)實(shí)現(xiàn)
  4. 往Hive表hbase_table_1表中插入數(shù)據(jù)

如何通過(guò) HiveSQL 來(lái)直接讀寫 HBase

當(dāng)前版本請(qǐng)閱讀以下參考資料,后期再行完善

  1. 如何整合hive和hbase
  2. HiveHbase集成實(shí)踐
  3. 更多進(jìn)階內(nèi)容請(qǐng)自行百度拓展查閱

Hive的分區(qū)和分桶

Hive的分區(qū)分桶都是數(shù)據(jù)存儲(chǔ)和組織的策略,分區(qū)類似文件的分類歸檔,分桶類似于傳統(tǒng)數(shù)據(jù)庫(kù)的索引

什么是Hive分區(qū)

  1. Hive中數(shù)據(jù)庫(kù),表,及分區(qū)都是在HDFS存儲(chǔ)的一個(gè)抽象
  2. Hive中的一個(gè)分區(qū)對(duì)應(yīng)的就是HDFS的一個(gè)目錄,目錄名就是分區(qū)字段
  3. 聲明分區(qū)表 PARTITIONED BY (name string),分區(qū)鍵不能和任何列重名
  4. 聲明數(shù)據(jù)要導(dǎo)入的分區(qū) PARTITION(name="fx67ll")
  5. 查看分區(qū) SHOW PARTITIONAS
  6. 根據(jù)分區(qū)查詢 WHERE name = "fx67ll"
  7. 指定切分格式
    ROW FORMAT DELIMITED
    # 每個(gè)字段之間由[ , ]分割
    FIELDS TERMINATED BY ','
    # 字段是Array形式,元素與元素之間由[ - ]分割
    COLLECTION ITEMS TERMINATED BY '-'
    # 字段是K-V形式,每組K-V對(duì)內(nèi)部由[ : ]分割
    MAP KEYS TERMINATED BY ':';
    

Hive分區(qū)的優(yōu)點(diǎn)

  1. 如果一個(gè)表中有大量的數(shù)據(jù),我們?nèi)磕贸鰜?lái)做查詞的功能,耗時(shí)比較長(zhǎng),查詢較慢,
    使用了分區(qū),就可以做到用到了那個(gè)分區(qū)就拿那個(gè)分區(qū)中的數(shù)據(jù)方便了查詢,提高了查詞的效率
  2. 橫向分配數(shù)據(jù),使得負(fù)載更為均衡

Hive分區(qū)的缺點(diǎn)

  1. 容易造成過(guò)多的小分區(qū),過(guò)多的目錄
  2. 如果分區(qū)策略不佳,容易導(dǎo)致分區(qū)數(shù)據(jù)不均衡,造成數(shù)據(jù)傾斜

什么是Hive分桶

  1. 分桶是相對(duì)分區(qū)進(jìn)行更細(xì)粒度的劃分,分桶將整個(gè)數(shù)據(jù)內(nèi)容按照某列屬性值得hash值進(jìn)行區(qū)分,類似于關(guān)系型數(shù)據(jù)的索引
  2. 如要安裝id屬性分為3個(gè)桶,就是對(duì)id屬性值的hash值對(duì)3取摸,按照取模結(jié)果對(duì)數(shù)據(jù)分桶,
    如取模結(jié)果為0的數(shù)據(jù)記錄存放到一個(gè)文件,取模為1的數(shù)據(jù)存放到一個(gè)文件,取模為2的數(shù)據(jù)存放到一個(gè)文件
  3. 分桶之前要執(zhí)行命令 set hive.enforce.bucketing = true
  4. 聲明分桶表 CLUSTERED BY(id) INTO 3 BUCKETS

關(guān)于Hive索引的說(shuō)明

  1. 即從3.0開始索引已經(jīng)被移除,有一些可替代的方案可能與索引類似:
    • 具有自動(dòng)重寫的物化視圖可以產(chǎn)生非常相似的結(jié)果,Hive2.3.0增加了對(duì)物化視圖視圖的支持
    • 使用列式文件格式((Parquet、ORC)–他們可以進(jìn)行選擇性掃描;甚至可以跳過(guò)整個(gè)文件/塊。很顯然,例如我們創(chuàng)建表時(shí)使用的ORC格式就已經(jīng)具有了索引的功能
  2. Hive為什么刪除了索引:
    • 由于Hive是針對(duì)海量數(shù)據(jù)存儲(chǔ)的,創(chuàng)建索引需要占用大量的空間,最主要的是Hive索引無(wú)法自動(dòng)進(jìn)行刷新,也就是當(dāng)新的數(shù)據(jù)加入時(shí)候,無(wú)法為這些數(shù)據(jù)自動(dòng)加入索引

Hive分桶的優(yōu)點(diǎn)

  1. 分桶字段需要根據(jù)業(yè)務(wù)進(jìn)行設(shè)定,可以解決數(shù)據(jù)傾斜問(wèn)題,主要是在關(guān)聯(lián)join的時(shí)候通過(guò)map端更快的連接
  2. 能夠提供類似的哈希的快速響應(yīng),比分區(qū)更快

Hive分桶的缺點(diǎn)

  1. 需要在建表時(shí)規(guī)劃好分桶策略,需要手動(dòng)加載數(shù)據(jù)到分桶表
  2. 本質(zhì)是空間換時(shí)間,時(shí)間換效率,所以在加載數(shù)據(jù)到表的時(shí)候有空間和時(shí)間上的消耗

Hive中靜態(tài)分區(qū)和動(dòng)態(tài)分區(qū)的區(qū)別

  1. 靜態(tài)分區(qū)與動(dòng)態(tài)分區(qū)的主要區(qū)別在于靜態(tài)分區(qū)是手動(dòng)指定,而動(dòng)態(tài)分區(qū)是通過(guò)數(shù)據(jù)來(lái)進(jìn)行判斷
  2. 詳細(xì)來(lái)說(shuō),靜態(tài)分區(qū)的列實(shí)在編譯時(shí)期,通過(guò)用戶傳遞來(lái)決定的;動(dòng)態(tài)分區(qū)只有在SQL執(zhí)行時(shí)才能決定
  3. 查詢和寫入的時(shí)候,靜態(tài)分區(qū)鍵要用 <static partition key> = <value> 指定分區(qū)值;動(dòng)態(tài)分區(qū)只需要給出分出分區(qū)鍵名稱 <dynamic partition key>
  4. 一張表可同時(shí)被靜態(tài)和動(dòng)態(tài)分區(qū)鍵分區(qū),只是動(dòng)態(tài)分區(qū)鍵需要放在靜態(tài)分區(qū)建的后面,因?yàn)镠DFS上的動(dòng)態(tài)分區(qū)目錄下不能包含靜態(tài)分區(qū)的子目錄

Hive動(dòng)態(tài)分區(qū)的參數(shù)設(shè)定

  1. 開啟動(dòng)態(tài)分區(qū)
    # 開啟動(dòng)態(tài)分區(qū)功能,默認(rèn)false  
    set hive.exec.dynamic.partition = true  
    # 允許所有分區(qū)都是動(dòng)態(tài)的,否則必須有靜態(tài)分區(qū)字段,默認(rèn)strict  
    set hive.exec.dynamic.partition.mode = nonstrict  
    
  2. 動(dòng)態(tài)分區(qū)參數(shù)調(diào)優(yōu)
    # 每個(gè)mapper或reducer可以允許創(chuàng)建的最大動(dòng)態(tài)分區(qū)個(gè)數(shù),默認(rèn)是100,超出則會(huì)報(bào)錯(cuò)  
    set hive.exec.max.dynamic.partitions.pernode = 1000
    # 一個(gè)動(dòng)態(tài)分區(qū)語(yǔ)句可以創(chuàng)建的最大動(dòng)態(tài)分區(qū)個(gè)數(shù),默認(rèn)是1000,超出報(bào)錯(cuò)
    set hive.exec.max.dynamic.partitions = 10000  
    # 全局可以創(chuàng)建的最大文件個(gè)數(shù),默認(rèn)是10000,超出報(bào)錯(cuò)  
    set hive.exec.max.created.files =100000  
    

Hive的內(nèi)部表和外部表

什么是Hive的內(nèi)部表和外部表

  1. 沒(méi)有external修飾,表數(shù)據(jù)保存在Hive默認(rèn)的路徑下,數(shù)據(jù)完全由Hive管理,刪除表時(shí)元數(shù)據(jù)(metadata)和表數(shù)據(jù)都會(huì)一起刪除
  2. external修飾,表數(shù)據(jù)保存在HDFS上,該位置由用戶指定,刪除表時(shí),只會(huì)刪除表的元數(shù)據(jù)(metadata)

Hive內(nèi)部表和外部表的區(qū)別是什么

  1. 內(nèi)部表數(shù)據(jù)由Hive自身管理,外部表數(shù)據(jù)由HDFS管理
  2. 內(nèi)部表數(shù)據(jù)存儲(chǔ)的位置是hive.metastore.warehouse.dir,默認(rèn)是 /user/hive/warehouse
  3. 外部表數(shù)據(jù)的存儲(chǔ)位置由自己制定,如果沒(méi)有LOCATION,Hive將在HDFS上的/user/hive/warehouse文件夾下以外部表的表名創(chuàng)建一個(gè)文件夾,并將屬于這個(gè)表的數(shù)據(jù)存放在這里
  4. 刪除內(nèi)部表會(huì)直接刪除元數(shù)據(jù)(metadata)及存儲(chǔ)數(shù)據(jù)
  5. 刪除外部表僅僅會(huì)刪除元數(shù)據(jù)(metadata),HDFS上的文件并不會(huì)被刪除
  6. 對(duì)內(nèi)部表的修改會(huì)將修改直接同步給元數(shù)據(jù)(metadata),而對(duì)外部表的表結(jié)構(gòu)和分區(qū)進(jìn)行修改,則需要修復(fù) MSCK REPAIR TABLE table_name

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

  1. 因?yàn)橥獠勘聿粫?huì)加載數(shù)據(jù)到Hive,減少數(shù)據(jù)傳輸,數(shù)據(jù)還能共享
  2. Hive不會(huì)修改數(shù)據(jù),所以無(wú)需擔(dān)心數(shù)據(jù)的損壞
  3. 刪除表時(shí),只刪除表結(jié)構(gòu),不刪除數(shù)據(jù)

Hive SQL

Hive中的SQL如何轉(zhuǎn)化成MapReduce任務(wù)的

  1. Antlr定義SQL的語(yǔ)法規(guī)則,完成SQL詞法,語(yǔ)法解析,將SQL轉(zhuǎn)化為抽象語(yǔ)法樹
  2. 遍歷抽象語(yǔ)法樹抽象出查詢的基本組成單元 QueryBlock
  3. 遍歷QueryBlock ,翻譯為執(zhí)行操作樹OperatorTree
  4. 邏輯層優(yōu)化器進(jìn)行OperatorTree變換,合并不必要的ReduceSinkOperator,減少shuffle數(shù)據(jù)量
  5. 遍厲OperatorTree,翻譯為MapReduce任務(wù)
  6. 物理層優(yōu)化器進(jìn)行MapReduce任務(wù)的變換,生成最終的執(zhí)行計(jì)劃

什么情況下Hive不走M(jìn)apReduce任務(wù)

Hive中如何查詢A表中B表不存在的數(shù)據(jù)

題目:A、B兩表,找出ID字段中,存在A表,但是不存在B表的數(shù)據(jù)。A表總共13w數(shù)據(jù),去重后大約3W條數(shù)據(jù),B表有2W條數(shù)據(jù),且B表的ID字段有索引

select * from  B
where (select count(1) as num from A where A.ID = B.ID) = 0

Hive中有哪些連接查詢以及如何使用

當(dāng)前版本請(qǐng)閱讀以下參考資料,后期再行完善

  1. Hive——join的使用
  2. 更多進(jìn)階內(nèi)容請(qǐng)自行百度拓展查閱

Hive中左連接和內(nèi)連接的區(qū)別

  1. 內(nèi)連接:連接的鍵匹配上就連接,沒(méi)有匹配上就過(guò)濾掉
  2. 左連接:以左表為基準(zhǔn),與右表做關(guān)聯(lián),關(guān)聯(lián)上則連接,右表關(guān)聯(lián)不上的則為null

Hive中左連接的底層原理

參考下面Hive查詢的時(shí)候on和where有什么區(qū)別的理解二

Hive查詢的時(shí)候 ON 和 WHERE 有什么區(qū)別

共同點(diǎn)

  1. on先執(zhí)行,where后執(zhí)行
  2. 并且where是對(duì)連接之后的結(jié)果進(jìn)行的查詢條件

第一種理解方式

  1. 條件不為主表?xiàng)l件的時(shí)候,放在on和where的后面一樣
  2. 條件為主表?xiàng)l件的時(shí)候,放在on后面,結(jié)果為主表全量,放在where后面的時(shí)候?yàn)橹鞅項(xiàng)l件篩選過(guò)后的全量
1. select * from a left join b on a.id = b.id and a.dt=20181115;
2. select * from a left join b on a.id = b.id and b.dt=20181115;
3. select * from a join b on a.id = b.id and a.dt=20181115;
4. select * from a left join b on a.id = b.id  where a.dt=20181115;
sql1: 如果是left join 在on上寫主表a的條件不會(huì)生效,全表掃描。
sql2: 如果是left join 在on上寫副表b的條件會(huì)生效,但是語(yǔ)義與寫到where 條件不同
sql3: 如果是inner join 在on上寫主表a、副表b的條件都會(huì)生效
sql4: 建議這么寫,大家寫sql大部分的語(yǔ)義都是先過(guò)濾數(shù)據(jù)然后再join,所以在不了解join on+條件的情況下,條件盡量別寫在on后,
直接寫到where后就ok了,如果where條件后寫b表的過(guò)濾條件,就變成了先left join出結(jié)果再按照b條件過(guò)濾數(shù)據(jù)  

第二種理解方式

  1. on是在生成連接表的起作用的,where是生成連接表之后對(duì)連接表再進(jìn)行過(guò)濾
  2. 當(dāng)使用left join時(shí),無(wú)論on的條件是否滿足,都會(huì)返回左表的所有記錄,對(duì)于滿足的條件的記錄,兩個(gè)表對(duì)應(yīng)的記錄會(huì)連接起來(lái),對(duì)于不滿足條件的記錄,那右表字段全部是null
  3. 當(dāng)使用right join時(shí),類似,只不過(guò)是全部返回右表的所有記錄
  4. 當(dāng)使用inner join時(shí),功能與where完全相同
經(jīng)過(guò)親測(cè)后,更加深了對(duì)on和where的理解,得出以下結(jié)論:

1.ON后的條件如果有過(guò)濾主表的條件,則結(jié)果對(duì)于不符合該條件的主表數(shù)據(jù)也會(huì)原條數(shù)保留,只是不匹配右表數(shù)據(jù)而已。對(duì)于on后面對(duì)右表的過(guò)濾條件,連接時(shí)會(huì)用該條件直接過(guò)濾右表數(shù)據(jù)后再和左邊進(jìn)行左連接??傊?,對(duì)于不滿足on后面的所有條件的數(shù)據(jù),左表會(huì)在結(jié)果數(shù)據(jù)中原條數(shù)保留數(shù)據(jù),只是不匹配右表數(shù)據(jù)而已。不滿足條件的右表數(shù)據(jù)各字段會(huì)直接以NULL連接主表。
2.ON后對(duì)左表的篩選條件對(duì)于結(jié)果行數(shù)會(huì)被忽略,但會(huì)影響結(jié)果中的匹配右表數(shù)據(jù),因?yàn)橹挥蟹献蟊項(xiàng)l件的數(shù)據(jù)才會(huì)去和符合條件的右表數(shù)據(jù)進(jìn)行匹配,不符合條件的左表數(shù)據(jù)會(huì)保留在最后結(jié)果中,但匹配的右表數(shù)據(jù)都是NULL.因此,對(duì)于需要過(guò)濾左表數(shù)據(jù)的話,需要把過(guò)濾條件放到where后面。
3.ON后的左表?xiàng)l件(單獨(dú)對(duì)左表進(jìn)行的篩選條件)對(duì)于結(jié)果行數(shù)無(wú)影響,還是會(huì)返回所有左表的數(shù)據(jù),但和右表匹配數(shù)據(jù)時(shí),系統(tǒng)只會(huì)拿左表符合條件(ON后的對(duì)左表過(guò)濾條件)的數(shù)據(jù)去和右表符合條件(ON后的對(duì)右表過(guò)濾條件)的數(shù)據(jù)進(jìn)行匹配抓取數(shù)據(jù),而不符合條件的左表數(shù)據(jù)還是會(huì)出現(xiàn)在結(jié)果列表中,只是對(duì)應(yīng)的右表數(shù)據(jù)都是NULL。
4.ON后的右表?xiàng)l件(單獨(dú)對(duì)右表進(jìn)行的篩選條件)會(huì)先對(duì)右表進(jìn)行數(shù)據(jù)篩選后再和左表做連接查詢,對(duì)結(jié)果行數(shù)有影響(當(dāng)左表對(duì)右表是一對(duì)多時(shí)),但不會(huì)影響左表的顯示行數(shù),然后拿符合條件的右表數(shù)據(jù)去和符合條件的左表數(shù)據(jù)進(jìn)行匹配。
5.Where還是對(duì)連接后的數(shù)據(jù)進(jìn)行過(guò)濾篩選,這個(gè)無(wú)異議。
6.匹配數(shù)據(jù)時(shí)無(wú)論左右表,都是拿符合ON后的過(guò)濾條件去做數(shù)據(jù)匹配,不符合的會(huì)保留左表數(shù)據(jù),用NULL填充右表數(shù)據(jù)。

綜上得出,ON后面對(duì)于左表的過(guò)濾條件,在最后結(jié)果行數(shù)中會(huì)被忽略,并不會(huì)先去過(guò)濾左表數(shù)據(jù)再連接查詢,但是ON后的右表?xiàng)l件會(huì)先過(guò)濾右表數(shù)據(jù)再連接左表進(jìn)行查詢。
連接查詢時(shí),都是用符合ON后的左右表的過(guò)濾條件的數(shù)據(jù)進(jìn)行連接查詢,只有符合左右表過(guò)濾條件的數(shù)據(jù)才能正確匹配,剩下的左表數(shù)據(jù)會(huì)正常出現(xiàn)在結(jié)果集中,但匹配的右表數(shù)據(jù)是NULL。因此對(duì)于左表的過(guò)濾條件切記要放到Where后,對(duì)于右表的過(guò)濾條件要看情況了。如果需要先過(guò)濾右表數(shù)據(jù)就把條件放到ON后面即可。

Hive 函數(shù)

關(guān)于 UDF/UDAF/UDTF 的提問(wèn)

  1. 如何使用UDF/UDAF/UDTF
  2. 為什么使用UDF/UDAF/UDTF
  3. 你寫過(guò)什么樣的UDF/UDAF/UDTF
  4. Hive自定義函數(shù)實(shí)現(xiàn)了什么函數(shù)
    上述四個(gè)問(wèn)題自行 參考資料 并結(jié)合工作中實(shí)際場(chǎng)景來(lái)作答,沒(méi)有標(biāo)準(zhǔn)答案

Hive中如何去重

第一種方式:使用 DISTINCT

  1. 對(duì)select 后面所有字段去重,并不能只對(duì)一列去重
  2. 當(dāng)DISTINCT應(yīng)用到多個(gè)字段的時(shí)候,DISTINCT必須放在開頭,其應(yīng)用的范圍是其后面的所有字段,而不只是緊挨著它的一個(gè)字段,而且DISTINCT只能放到所有字段的前面
  3. DISTINCT對(duì)NULL是不進(jìn)行過(guò)濾的,即返回的結(jié)果中是包含NULL值的
  4. 聚合函數(shù)中的DISTINCT,如count()會(huì)過(guò)濾掉為NULL

第二種方式:使用 GROUP BY

  1. 對(duì)GROUP BY后面所有字段去重,并不能只對(duì)一列去重

第三種方式:使用 ROW_NUMBER() OVER 窗口函數(shù)

  1. 參考資料一:一種巧妙的hive sql數(shù)據(jù)去重方法
  2. 參考資料二:Hive--數(shù)據(jù)去重及row_number()
  3. 參考資料三:Hive(十一)--數(shù)據(jù)去重及row_number()

Hive中排序函數(shù)的使用方式及區(qū)別

  1. order by 會(huì)對(duì)輸入做全局排序,為保證全局的排序,因此只有一個(gè)reducer,會(huì)導(dǎo)致當(dāng)輸入規(guī)模較大時(shí),需要較長(zhǎng)的計(jì)算時(shí)間。
  2. sort by不是全局排序,其在數(shù)據(jù)進(jìn)入reducer前完成排序。因此,如果用sort by進(jìn)行排序,則sort by只保證每個(gè)reducer的輸出有序,不保證全局有序。
  3. distribute by 字段 根據(jù)指定的字段將數(shù)據(jù)分到不同的reducer,且分發(fā)算法是hash散列,常用sort by結(jié)合使用,Hive要求distribute by語(yǔ)句要寫在sort by語(yǔ)句之前。
  4. cluster by 字段 除了具有distribute by的功能(既可以把數(shù)據(jù)分到不同的reduce)外,還會(huì)對(duì)該字段進(jìn)行排序。但是排序只能是倒序排序,不能指定排序規(guī)則為asc或者desc
  5. 因此:
    • 當(dāng)數(shù)據(jù)量規(guī)模較大時(shí),不使用 order by,使用用 distribute by + sort by
    • 如果 distribute bysort by 字段是同一個(gè)時(shí),此時(shí),cluster by = distribute by + sort by

Hive中部分高頻函數(shù) ———— split / coalesce / collect list / collect set

  1. Hive ———— split
  2. Hive ———— coalesce
  3. Hive ———— collect list/collect set

Hive常用函數(shù)

  1. Hive常用的函數(shù)總結(jié)
  2. Hive函數(shù)大全

Hive 運(yùn)維

如何監(jiān)控一個(gè)提交后的Hive狀態(tài)

  1. 使用java代碼提交Hive,通過(guò)HiveStatement獲取日志數(shù)據(jù)并解析出application_id
  2. 就可以通過(guò)application_id去yarn上查看運(yùn)行狀態(tài)

Hive 優(yōu)化

該模塊請(qǐng)參考我關(guān)于Hive優(yōu)化的文章

  1. 點(diǎn)擊訪問(wèn) ————> Hive在工作中的調(diào)優(yōu)總結(jié)
  2. 點(diǎn)擊訪問(wèn) ————> HiveSQL工作實(shí)戰(zhàn)總結(jié)

我是 fx67ll.com,如果您發(fā)現(xiàn)本文有什么錯(cuò)誤,歡迎在評(píng)論區(qū)討論指正,感謝您的閱讀!
如果您喜歡這篇文章,歡迎訪問(wèn)我的 本文github倉(cāng)庫(kù)地址,為我點(diǎn)一顆Star,Thanks~ :)
轉(zhuǎn)發(fā)請(qǐng)注明參考文章地址,非常感謝?。?!

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容