大數據開發-生產中遇到的10個致命問題

生產環境版本 Hive: 1.2.1, Spark: 2.3.2

1.insert overwrite directory 不會覆蓋數據

注意,生成結果是目錄,生成目錄裏面的不同文件名不會被覆蓋,因此很容易出現數據double或者沒有覆蓋到數據的問題,比如數據分片原始結果如下:

/mytable/000000_0
/mytable/000000_1
/mytable/000000_2
/mytable/000000_3 
## 新生成的數據只含有 000000_0分片,那麼1 2 3分片並不會被刪掉

解決方式:使用目錄上面建立外表insertoverwrite, 如果這個目錄是導入其他系統的目錄文件,可能更致命。注意建立外表時候,如果是分區表,刪除掉分區,然後insert overwrite也會導致數據重複,測試版本2.3.2

//a文件數據內容
//2  10
//2  10
//2  10
//創建管理表
create table t2(id int) partitioned by (dt string);
load data local inpath '/xxx/a'

// 創建外分區表
create external table t1(id int) partitioned by (dt string);
// overwrite分區10
insert overwrite table t1 partition(dt='10') select 1 from t2 where dt=10;
//刪除分區10
ALTER TABLE t1 DROP PARTITION(dt='10');
// overwrite 10這個分區
insert overwrite table t1 partition(dt='10') select 2 from t2 where dt=10;

結果顯示6條數據,顯然是異常的,在hive中這個結果是正常的(ps,最後發現是由於組內小夥伴自己調試原因,改了參數,其實默認參數是沒問題的,就是true)

解決方式:

set spark.sql.hive.convertMetastoreParquet=true
set spark.sql.hive.convertMetastoreOrc=true

2.insert tableA select xx from tableB 字段屬性問題

注意:select 的字段名即使和tableA中的字段一樣,但是插入順序與字段順序不一樣,那麼結果也是有問題的

常見發生情況爲 later view 下的select,以爲字段名對了,插入就沒問題,實際上是按順序插入的,即select再插入,也許與底層是mr實現有關係

3.spark.write 寫文件如果是覆蓋寫,如果是併發多個寫,同樣可能會出現數據重複

通過spark.write寫的方式,然後再與表關聯,寫路徑的方式主要有下面兩種,通過參數:mapreduce.fileoutputcommitter.algorithm.version 來控制,

Spark寫入HDFS是依賴於Hadoop的FileOutputCommitter的,它有兩種算法,一種是直接寫到臨時目錄,再copy到輸出目錄,稱之爲v1版本,另外一種是直接輸出到結果目錄,稱之爲v2版本,輸出性能上v2版本好一點,但是這兩種都不能保證最終一致性,也就是在併發寫,情況下都是有問題的,其中v1是兩階段提交,可以保證讀的時候,不是髒數據

具體可參考:https://blog.csdn.net/u013332124/article/details/92001346

常發生於任務剛啓動,然後停止,又立刻重新調度

注意:離線的事務性比較差,多注意一下,儘量不要立刻重啓任務,讓同一個目錄寫數據的操作同時進行,如果一定要馬上重啓調度任務,解決方式是kill掉任務,刪除_remporay文件即可,離線情況下一致性往往大於性能,拒自己情況選擇v1還是v2版本的提交。

4.msck repair table 的問題

在回溯數據和例行執行任務時候,可能會出現寫數據還沒寫完,回溯的任務將表msck repair ,導致下游任務讀到的數據不完全,跟3問題類似,其中兩個version的提交,在msck repair 之後,都會出現,數據不完全,下游任務就檢測到分區

解決方式

write + repair —> insert / write + add partition

5.insert overwrite自個表分區

在hive裏面直接寫是支持的,在spark裏面會先刪數據分區,然後懶加載纔開始讀數據,這樣就導致空指針,空數據問題,解決方法是先cache + 隨便一個action 數據緩存到內存。

注意:一般這樣不建議,除非是剛開始開發的不重要的表,離線的做法是增加version來區分版本,常用在增加字段,修改字段生成邏輯,先增加個version,在新version上來搞,搞完了通知下游切換,或者保證無誤,將version改到老分區。

6.切記用最近的邏輯來依賴分區

實際場景中,要儘可能地讓數據跑批冪等,否則會出現在不知情維護情況下出現線上問題,增量表冪等常用處理方式ods_day ≤ 當天 ,全量表常用處理方式ods_day=當天即使並不需要卡這個時間,在當前天修數據,或者回溯時候,就不能保證數據一直有效,不能回溯的數據,查問題會讓你噁心至極。

7.Spark和Hive的不兼容問題

對於Orc文件格式和Parquet文件格式,從兼容上來說,Spark實現更新一點,Hive稍微舊一點,比如用Spark建立的分區在某些情況寫出現null, empty等問題,可以設置下面兩個參數,開啓使用hive的orc和Parquet格式來建表,這樣Spark和Hive就可以讀到可以同時兼容的數據表了,建議除非一些臨時表,需要落庫的表,可以儘量用Hive的方式來建,建完以後,Spark寫的時候多加下面的參數就不會有兼容問題了

//第一種
set spark.sql.hive.convertMetastoreParquet=true
set spark.sql.hive.convertMetastoreOrc=true 

//第二種
// Spark使用與Hive相同的約定寫parquet數據
.config("spark.sql.parquet.writeLegacyFormat", "true") 

8.讀文件Qps過高問題

20/12/07 19:39:17 ERROR executor.Executor: Exception in task 740.2 in stage 2.0 (TID 5250)
org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 16,384; received: 0)

遇到如圖所示問,從表面展示出來的是,讀文件太高,導致傳輸只傳輸了一半數據,與機器限流有關,就我廠而言,問過bmr維護的人,給的答覆是,減少對某一文件的讀取頻率

解決方式:剛開始有些鬱悶,離線數據讀難道還有併發問題,其實是限制的在某個datanode上某個文件塊的讀取qps, 所以,從這些方面來排查,首先分析哪些表是關鍵表,對關鍵表看看是否都是大文件,如果是的話調整其爲小文件,常見方式有repatition 輸出或者hive的reduce數。

9.排序窗口函數的bug

經過多次測試,-這個符號不起作用

spark.sql(sql).withColumn("rn", row_number().over(Window.partitionBy('f_trans_id).orderBy(-'f_modify_time)))
// 替代方案(最好用sql形式而不是DSL,感覺不是很直觀)
spark.sql(sql).withColumn("rn", row_number().over(Window.partitionBy('f_trans_id).orderBy($"f_modify_time".desc))
spark.sql(sql).withColumn("rn", row_number().over(Window.partitionBy('f_trans_id).orderBy(col(f_modify_time).desc))

10.alter table cascase

alter table 要加CASCADE,否則回溯的歷史數據分區的數據在hive中看不到,原理是不加cascade只對加了字段以後的元數據有用,對於舊分區回溯,即使數據回溯完成,你也是拿不到的,如果在回溯完才發現這個問題,可以change colunmn的方式來補元數據

參考

https://www.turbofei.wang/spark/2019/09/30/關於Spark數據計算結果異常的場景分析

https://blog.csdn.net/u013332124/article/details/92001346

吳邪,小三爺,混跡於後臺,大數據,人工智能領域的小菜鳥。
更多請關注
file

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章