當前位置:首頁 » 編程語言 » 必然數據傾斜的sql如何優化
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

必然數據傾斜的sql如何優化

發布時間: 2023-01-14 08:14:36

A. hive中sql運算出現傾斜的情況,主要原因有哪些

1. Mapjoin是一種避免避免數據傾斜的手段

允許在map階段進行join操作,MapJoin把小表全部讀入內存中,在map階段直接拿另外一個表的數據和內存中表數據做匹配,由於在map是進行了join操作,省去了rece運行的效率也會高很多

在《hive:join遇到問題》有具體操作

在對多個表join連接操作時,將小表放在join的左邊,大表放在Jion的右邊,

在執行這樣的join連接時小表中的數據會被緩存到內存當中,這樣可以有效減少發生內存溢出錯誤的幾率

2. 設置參數

hive.map.aggr = true

hive.groupby.skewindata=true 還有其他參數

3.SQL語言調節

比如: group by維度過小時:採用sum() group by的方式來替換count(distinct)完成計算

4.StreamTable

將在recer中進行join操作時的小table放入內存,而大table通過stream方式讀取

B. 數據分析課程筆記 - 19 - HiveSQL 常用優化技巧

大家好呀,這節課學習 HiveSQL 的常用優化技巧。由於 Hive 主要用來處理非常大的數據,運行過程由於通常要經過 MapRece 的過程,因此不像 MySQL 一樣很快出結果。而使用不同方法寫出來的 HiveSQL 語句執行效率也是不一樣的,因此為了減少等待的時間,提高伺服器的運行效率,我們需要在 HiveSQL 的語句上進行一些優化。

本節課的主要內容

引言
1、技巧一:列裁剪和分區裁剪
(1)列裁剪
(2)分區裁剪
2、技巧二:排序技巧——sort by代替order by
3、技巧三:去重技巧——用group by來替換distinct
4、技巧四:聚合技巧——grouping sets、cube、rollup
(1)grouping sets
(2)cube
(3)rollup
5、技巧五:換個思路解題
6、技巧六:union all時可以開啟並發執行
7、技巧七:表連接優化
8、技巧八:遵循嚴格模式

Hive 作為大數據領域常用的數據倉庫組件,在平時設計和查詢時要特別注意效率。影響Hive效率的幾乎從不是數據量過大,而是數據傾斜、數據冗餘、job 或 I/O 過多、MapRece 分配不合理等等。對 Hive 的調優既包含對HiveSQL 語句本身的優化,也包含 Hive 配置項和 MR 方面的調整。

列裁剪就是在查詢時只讀取需要的列。當列很多或者數據量很大時,如果select 所有的列或者不指定分區,導致的全表掃描和全分區掃描效率都很低。Hive中與列裁剪優化相關的配置項是 hive.optimize.cp ,默認是 true 。

分區裁剪就是在查詢時只讀需要的分區。Hive中與分區裁剪優化相關的則是 hive.optimize.pruner ,默認是 true 。

HiveSQL中的 order by 與其他 SQL 語言中的功能一樣,就是將結果按某個欄位全局排序,這會導致所有map端數據都進入一個 rece 中,在數據量大時可能會長時間計算不完。

如果使用 sort by ,那麼就會視情況啟動多個 recer 進行排序,並且保證每個 recer 內局部有序。為了控制 map 端數據分配到 rece 的 key,往往還要配合 distribute by 一同使用。如果不加 distribute by 的話,map 端數據就會隨機分配給 recer。

這里需要解釋一下, distribute by 和 sort by 結合使用是如何相較於 order by 提升運行效率的。

假如我們要對一張很大的用戶信息表按照年齡進行分組,優化前的寫法是直接 order by age 。使用 distribute by 和 sort by 結合進行優化的時候, sort by 後面還是 age 這個排序欄位, distribute by 後面選擇一個沒有重復值的均勻欄位,比如 user_id 。

這樣做的原因是,通常用戶的年齡分布是不均勻的,比如20歲以下和50歲以上的人非常少,中間幾個年齡段的人又非常多,在 Map 階段就會造成有些任務很大,有些任務很小。那通過 distribute by 一個均勻欄位,就可以讓系統均勻地進行「分桶」,對每個桶進行排序,最後再組合,這樣就能從整體上提升 MapRece 的效率。

取出 user_trade 表中全部支付用戶:

原有寫法的執行時長:

優化寫法的執行時長:

考慮對之前的案例進行優化:

注意: 在極大的數據量(且很多重復值)時,可以先 group by 去重,再 count() 計數,效率高於直接 count(distinct **) 。

如果我們想知道用戶的性別分布、城市分布、等級分布,你會怎麼寫?

通常寫法:

缺點 :要分別寫三次SQL,需要執行三次,重復工作,且費時。

那該怎麼優化呢?

注意 :這個聚合結果相當於縱向地堆在一起了(Union all),分類欄位用不同列來進行區分,也就是每一行數據都包含 4 列,前三列是分類欄位,最後一列是聚合計算的結果。

GROUPING SETS() :在 group by 查詢中,根據不同的維度組合進行聚合,等價於將不同維度的 group by 結果集進行 union all。聚合規則在括弧中進行指定。

如果我們想知道用戶的性別分布以及每個性別的城市分布,你會怎麼寫?

那該怎麼優化呢?

注意: 第二列為NULL的,就是性別的用戶分布,其餘有城市的均為每個性別的城市分布。

cube:根據 group by 維度的所有組合進行聚合

注意 :跑完數據後,整理很關鍵!!!

rollup:以最左側的維度為主,進行層級聚合,是cube的子集。

如果我想同時計算出,每個月的支付金額,以及每年的總支付金額,該怎麼辦?

那應該如何優化呢?

條條大路通羅馬,寫SQL亦是如此,能達到同樣效果的SQL有很多種,要學會思路轉換,靈活應用。

來看一個我們之前做過的案例:

有沒有別的寫法呢?

Hive 中互相沒有依賴關系的 job 間是可以並行執行的,最典型的就是
多個子查詢union all。在集群資源相對充足的情況下,可以開啟並
行執行。參數設置: set hive.exec.parallel=true;

時間對比:

所謂嚴格模式,就是強制不允許用戶執行3種有風險的 HiveSQL 語句,一旦執行會直接報錯。

要開啟嚴格模式,需要將參數 hive.mapred.mode 設為 strict 。

好啦,這節課的內容就是這些。以上優化技巧需要大家在平時的練習和使用中有意識地去注意自己的語句,不斷改進,就能掌握最優的寫法。

C. 關於SQL資料庫優化

具體要注意的:
1.應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
2.應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行。
3.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
4.in 和 not in 也要慎用,因為IN會使系統無法使用索引,而只能直接搜索表中的數據。如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
5.盡量避免在索引過的字元數據中,使用非打頭字母搜索。這也使得引擎無法利用索引。
見如下例子:
SELECT * FROM T1 WHERE NAME LIKE 『%L%』
SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=』L』
SELECT * FROM T1 WHERE NAME LIKE 『L%』
即使NAME欄位建有索引,前兩個查詢依然無法利用索引完成加快操作,引擎不得不對全表所有數據逐條操作來完成任務。而第三個查詢能夠使用索引來加快操作。
6.必要時強制查詢優化器使用某個索引,如在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變數,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
7.應盡量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
SELECT * FROM T1 WHERE F1/2=100
應改為:
SELECT * FROM T1 WHERE F1=100*2
SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=』5378』
應改為:
SELECT * FROM RECORD WHERE CARD_NO LIKE 『5378%』
SELECT member_number, first_name, last_name FROM members
WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21
應改為:
SELECT member_number, first_name, last_name FROM members
WHERE dateofbirth < DATEADD(yy,-21,GETDATE())
即:任何對列的操作都將導致表掃描,它包括資料庫函數、計算表達式等等,查詢時要盡可能將操作移至等號右邊。
8.應盡量避免在where子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)='abc'--name以abc開頭的id
select id from t where datediff(day,createdate,'2005-11-30')=0--『2005-11-30』生成的id
應改為:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
9.不要在 where 子句中的「=」左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
10.在使用索引欄位作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓欄位順序與索引順序相一致。
11.很多時候用 exists是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select top 1 from b where num=a.num)
SELECT SUM(T1.C1)FROM T1 WHERE(
(SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)
SELECT SUM(T1.C1) FROM T1WHERE EXISTS(
SELECT * FROM T2 WHERE T2.C2=T1.C2)
兩者產生相同的結果,但是後者的效率顯然要高於前者。因為後者不會產生大量鎖定的表掃描或是索引掃描。
如果你想校驗表裡是否存在某條紀錄,不要用count(*)那樣效率很低,而且浪費伺服器資源。可以用EXISTS代替。如:
IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx')
可以寫成:
IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')
經常需要寫一個T_SQL語句比較一個父結果集和子結果集,從而找到是否存在在父結果集中有而在子結果集中沒有的記錄,如:
SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用別名a代替
WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)
SELECT a.hdr_key FROM hdr_tbl a
LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL
SELECT hdr_key FROM hdr_tbl
WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)
三種寫法都可以得到同樣正確的結果,但是效率依次降低。
12.盡量使用表變數來代替臨時表。如果表變數包含大量數據,請注意索引非常有限(只有主鍵索引)。
13.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
14.臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。
15.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
16.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

17.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
18.盡量避免大事務操作,提高系統並發能力。
19.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

20. 避免使用不兼容的數據類型。例如float和int、char和varchar、binary和varbinary是不兼容的。數據類型的不兼容可能使優化器無法執行一些本來可以進行的優化操作。例如:
SELECT name FROM employee WHERE salary > 60000
在這條語句中,如salary欄位是money型的,則優化器很難對其進行優化,因為60000是個整型數。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。
21.充分利用連接條件,在某種情況下,兩個表之間可能不只一個的連接條件,這時在 WHERE 子句中將連接條件完整的寫上,有可能大大提高查詢速度。
例:
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO
第二句將比第一句執行快得多。
22、使用視圖加速查詢
把表的一個子集進行排序並創建視圖,有時能加速查詢。它有助於避免多重排序 操作,而且在其他方面還能簡化優化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>「98000」
ORDER BY cust.name
如果這個查詢要被執行多次而不止一次,可以把所有未付款的客戶找出來放在一個視圖中,並按客戶的名字進行排序:
CREATE VIEW DBO.V_CUST_RCVLBES
AS
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
然後以下面的方式在視圖中查詢:
SELECT * FROM V_CUST_RCVLBES
WHERE postcode>「98000」
視圖中的行要比主表中的行少,而且物理順序就是所要求的順序,減少了磁碟I/O,所以查詢工作量可以得到大幅減少。
23、能用DISTINCT的就不用GROUP BY
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID
可改為:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
24.能用UNION ALL就不要用UNION
UNION ALL不執行SELECT DISTINCT函數,這樣就會減少很多不必要的資源

35.盡量不要用SELECT INTO語句。
SELECT INOT 語句會導致表鎖定,阻止其他用戶訪問該表。
上面我們提到的是一些基本的提高查詢速度的注意事項,但是在更多的情況下,往往需要反復試驗比較不同的語句以得到最佳方案。最好的方法當然是測試,看實現相同功能的SQL語句哪個執行時間最少,但是資料庫中如果數據量很少,是比較不出來的,這時可以用查看執行計劃,即:把實現相同功能的多條SQL語句考到查詢分析器,按CTRL+L看查所利用的索引,表掃描次數(這兩個對性能影響最大),總體上看詢成本百分比即可。

今天在itput上看了一篇文章,是討論一個語句的優化:
原貼地址: http://www.itpub.net/viewthread.php?tid=1015964&extra=&page=1
一,發現問題 優化的語句:
請問以下語句如何優化:
CREATE TABLE aa_001
( ip VARCHAR2(28),
name VARCHAR2(10),
password VARCHAR2(30) )

select * from aa_001 where ip in (1,2,3) order by name desc;
--目前表中記錄有一千多萬條左右,而且in中的值個數是不確定的。
以上就是優化的需要優化的語句和情況。

不少人在後面跟帖:有的說沒辦法優化,有的說將IN該為EXISTS,有的說在ip上建立索引復合索引(ip,name)等等。
二,提出問題 那這樣的情況,能優化嗎,如何優化?今天就來討論這個問題。
三,分析問題 1,數據量1千萬多條。
2,in中的值個數是不確定
3.1 分析數據分布 這里作者沒有提到ip列的數據的分布情況,目前ip列的數據分布可能有以下幾種:
1,ip列(數據唯一,或者數據重復的概率很小)
2,ip列 (數據不均勻,可能有些數據重復多,有些重復少)
3,ip列 (數據分布比較均勻,數據大量重復,主要就是一些同樣的數據(可能只有上萬級別不同的ip數據等)

解決問題:
1,對於第一種數據分布情況,只要在ip列建立一個索引即可。這時不管表有多少行, in個數是不確定的情況下,都很快。
2,對應第二中數據分布情況,在ip列建立索引,效果不好。因為數據分布不均勻,可能有些快,有些慢
3,對應第三種數據分布情況,在ip列建立索引,速度肯定慢。
注意:這里的 order by name desc 是在取出數據後再排序的。而不是取數據前排序

對於2,3兩個情況,因為都是可能需要取出大量的數據,優化器就採用表掃描(table scan),而不是索引查找(index seek) ,速度很慢,因為這時表掃描效率要優於索引查找,特別是高並發情況下,效率很低。

那對應2,3中情況,如何處理。是將in改成exists。其實在sql server 2005和oracle里的優化器在in後面數據少時,效率是一樣的。這時採用一般的索引效率很低。這時如果在ip列上建立聚集索引,效率會比較高。我們在SQL server 2005中做個測試。

表:[dbo].[[zping.com]]]中有約200萬條數據。包含列Userid, id, Ruleid等列。按照上面的情況查詢一下類似語句:
select * from [dbo].[[zping.com]]] where
userid in ('',''
,'') order by Ruleid desc

我們先看userid的數據分布情況,執行下面語句:
select userid,count(*) from [dbo].[[zping.com]]] group by userid order by 2
這時我們看看數據分布:總共有379條數據,數據兩從1到15萬都有,數據分布傾斜嚴重。下圖是其中一部分。

這時如果在ip上建立非聚集索引,效率很低,而且就是強行索引掃描,效率也很低,會發現IO次數比表掃描還高。這時只能在ip上建立聚集索引。這時看看結果。

這時發現,搜索採用了(clustered index seek)聚集搜索掃描。
在看看查詢返回的結果:
(156603 行受影響)
表 '[zping.com]'。掃描計數 8,邏輯讀取 5877 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
表 'Worktable'。掃描計數 0,邏輯讀取 0 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
返回15萬行,才不到6千次IO。效率較高,因為這15萬行要排序,查詢成本里排序佔了51%。當然可以建立(userid,Ruleid)復合聚集索引,提高性能,但這樣做DML維護成本較高。建議不採用。

從上面的測試例子可以看出, 優化的解決辦法:
數據分布為1:建立ip索引即可
數據分布為2,3:在ip列建立聚集索引。

D. Hive SQL處理數據傾斜的方法有哪些

可以在查詢的結尾加上diistribute by rand(),回答如有幫助請採納,謝謝!

E. 一條sql執行過長的時間,你如何優化,從哪些方面

1、查看sql是否涉及多表的聯表或者子查詢,如果有,看是否能進行業務拆分,相關欄位冗餘或者合並成臨時表(業務和演算法的優化)

2、涉及鏈表的查詢,是否能進行分表查詢,單表查詢之後的結果進行欄位整合

3、如果以上兩種都不能操作,非要鏈表查詢,那麼考慮對相對應的查詢條件做索引。加快查詢速度

4、針對數量大的表進行歷史表分離(如交易流水表)

5、資料庫主從分離,讀寫分離,降低讀寫針對同一表同時的壓力,至於主從同步,mysql有自帶的binlog實現 主從同步

6、explain分析sql語句,查看執行計劃,分析索引是否用上,分析掃描行數等等

7、查看mysql執行日誌,看看是否有其他方面的問題

個人理解:從根本上來說,查詢慢是佔用mysql內存比較多,那麼可以從這方面去酌手考慮

F. 如何進行SQL性能優化

這里分享下mysql優化的幾種方法。

1、首先在打開的軟體中,需要分別為每一個表創建 InnoDB FILE的文件。

G. hive數據傾斜及處理

火山日常啰嗦
學習了一些大數據的相關框架後,發現應用層的東西確實不難,真正難的都是底層原理,所以我查看了很多資料,借鑒了前人的方法再加上自己的理解,寫下了這篇文章。

數據傾斜的直白概念:
數據傾斜就是數據的分布不平衡,某些地方特別多,某些地方又特別少,導致的在處理數據的時候,有些很快就處理完了,而有些又遲遲未能處理完,導致整體任務最終遲遲無法完成,這種現象就是數據傾斜。

針對maprece的過程來說就是,有多個rece,其中有一個或者若干個rece要處理的數據量特別大,而其他的rece處理的數據量則比較小,那麼這些數據量小的rece很快就可以完成,而數據量大的則需要很多時間,導致整個任務一直在等它而遲遲無法完成。
跑mr任務時常見的rece的進度總是卡在99%,這種現象很大可能就是數據傾斜造成的。

產生數據傾斜的原因:
1) key的分布不均勻或者說某些key太集中。
上面就說過,rece的數據量大小差異過大,而rece的數據是分區的結果,分區是對key求hash值,根據hash值決定該key被分到某個分區,進而進入到某個rece,而如果key很集中或者相同,那麼計算得到它們的hash值可能一樣,那麼就會被分配到同一個rece,就會造成這個rece所要處理的數據量過大。
2) 業務數據自身的特性。
比如某些業務數據作為key的欄位本就很集中,那麼結果肯定會導致數據傾斜啊。
還有其他的一些原因,但是,根本原因還是key的分布不均勻,而其他的原因就是會造成key不均勻,進而導致數據傾斜的後果,所以說根本原因是key的分布不均勻。

既然有數據傾斜這種現象,就必須要有數據傾斜對應的處理方案啊。
簡單地說數據傾斜這種現象導致的任務遲遲不能完成,耗費了太多時間,極大地影響了性能,所以我們數據傾斜的解決方案設計思路就是往如何提高性能,即如何縮短任務的處理時間這方面考慮的,而要提高性能,就要讓key分布相對均衡,所以我們的終極目標就是考慮如何預處理數據才能夠使得它的key分布均勻。

常見的數據傾斜處理方案:
1 設置參數
1)設置hive.map.aggr=true //開啟map端部分聚合功能,就是將key相同的歸到一起,減少數據量,這樣就可以相對地減少進入rece的數據量,在一定程度上可以提高性能,當然,如果數據的減少量微乎其微,那對性能的影響幾乎沒啥變化。
2)設置hive.groupby.skewindata=true //如果發生了數據傾斜就可以通過它來進行負載均衡。當選項設定為 true,生成的查詢計劃會有兩個 MR Job。第一個 MR Job 中,Map 的輸出結果集合會隨機分布到 Rece 中,每個 Rece 做部分聚合操作,並輸出結果,這樣處理的結果是相同的Key 有可能被分發到不同的 Rece 中,從而達到負載均衡的目的;第二個 MR Job 再根據預處理的數據結果按照Key 分布到 Rece 中(這個過程是按照key的hash值進行分區的,不同於mr job1的隨機分配,這次可以保證相同的Key 被分布到同一個 Rece 中),最後完成最終的聚合操作。所以它主要就是先通過第一個mr job將key隨機分配到rece,使得會造成數據傾斜的key可能被分配到不同的rece上,從而達到負載均衡的目的。到第二個mr job中,因為第一個mr job已經在rece中對這些數據進行了部分聚合(就像單詞統計的例子,a這個字母在不同的rece中,已經算出它在每個rece中的個數,但是最終的總的個數還沒算出來,那麼就將它傳到第二個mr job,這樣就可以得到總的單詞個數),所以這里直接進行最後的聚合就可以了。
3)hive.exec.recers.bytes.per.recer=1000000000 (單位是位元組)
每個rece能夠處理的數據量大小,默認是1G
4)hive.exec.recers.max=999
最大可以開啟的rece個數,默認是999個
在只配了hive.exec.recers.bytes.per.recer以及hive.exec.recers.max的情況下,實際的rece個數會根據實際的數據總量/每個rece處理的數據量來決定。
5)mapred.rece.tasks=-1
實際運行的rece個數,默認是-1,可以認為指定,但是如果認為在此指定了,那麼就不會通過實際的總數據量/hive.exec.recers.bytes.per.recer來決定rece的個數了。

2 sql語句優化
給幾個具體的場景以及在這些場景下的處理方案:
1)進行表的join這種業務操作時,經常會產生數據傾斜。
原因就是這些業務數據本就存在key會分布不均勻的風險,所以我們join時不能使用普通的join(rece端join)或者可以使用普通join,但是是優化後的。

但是這種操作有個前提條件就是僅適用於小表join大表,而小表怎麼定義它的大小,多小的表才算小表,這里有個參數可以確定的(但是這個參數名我暫時忘記了),如果小表的數據大小小於這個值,就可以使用map join,而是在這種情況下是自動使用map join這種方案的。所以如果是大小表join,直接用map join,避免數據傾斜。

方法1:(普通join)
select * from log a join users b on (a.user_id is not null and a.user_id = b.user_id );
這是屬於表的內連接的,兩張表不滿足條件的記錄都不保留。

方法2:檢測到user_id是null時給它賦予一個新值(這個新值由一個字元串(比如我自己給它定一個 hive)加上一個隨機數組成),這樣就可以將原來集中的key分散開來,也避免了數據傾斜的風險。而且因為這些數據本來就是無效數據,根本不會出現在結果表中,所以,這樣處理user_id(由一個字元串(比如我自己給它定一個 hive)加上一個隨機數),它也無法關聯的,因為有效的數據的user_id沒有這種形式的,所以就算這些無效數據出現在不同的rece中還是不會影響結果的,我這樣處理只是為了將它們分散開而已,所以用這種方法處理,結果表中也不會出現null這些無效數據,跟過濾處理方案得到的結果是一樣的。(普通join)
select *
from log a
join users b
on case when a.user_id is null then concat(『hive』,rand() ) else a.user_id end = b.user_id;
但是這兩種方案只是適用於大表join大表的內連接,兩張表的無效數據都不保留。
但是如果對於左外連接或者右外連接這種情況,即使驅動表中某些記錄在另一張表中沒有數據與它對應,但我們是依然需要保留驅動表的這些數據的,那該怎麼辦呢?其實很簡單,只需要將上述方法得到的結果再與驅動表的這些無數據取並集就可以了。
如下:
select * from log a
left outer join users b
on a.user_id is not null
and a.user_id = b.user_id
union all
select * from log a
where a.user_id is null;
2)雖然都是大表,但是呢對於某些業務數據而言,其有用的部分只佔它所在表的很少一部分,那麼我們就可以將它們先取出來,得到的結果應該是一張小表,那麼就可以使用map join來避免數據傾斜了。

場景:用戶表中user_id欄位為int,log表中user_id欄位既有string類型也有int類型。
當按照user_id進行兩個表的Join操作時,因為我們在連接時要進行user_id的比較,所以需要user_id的類型都相同,如果我們選擇將log表中的String類型轉換為int類型,那麼就可能會出現這種情況:String類型轉換為int類型得到的都是null值(這就是類型轉換的問題了,String類型數據轉換為int類型會失敗,數據丟失,就會賦null值),如果所有的String類型的user_id都變成了null,那麼就又出現了集中的key,分區後就又會導致數據傾斜。所以我們進行類型轉換時不能選擇將String類型轉換為int,而應該將int類型轉換為String,因為int轉換為String不會出問題,int類型原來的值是什麼,轉換為String後對應的字元串就會是什麼,形式沒變,只是類型變了而已。
解決方法:把int類型轉換成字元串類型
select * from users a
join logs b
on (a.usr_id = cast(b.user_id as string));

比如有一份日誌,要你從日誌中統計某天有多少個用戶訪問網站,即統計有多少個不同的user_id;但是呢這個網站卻又恰巧遭到攻擊,日誌中大部分都是同一個user_id的記錄,其他的user_id屬於正常訪問,訪問量不會很大,在這種情況下,當你直接使用count(distinct user_id)時,這也是要跑mr任務的啊,這時這些大量的相同的user_id就是集中的key了,結果就是通過分區它們都被分到一個rece中,就會造成這個rece處理的數據特別大,而其中的rece處理的數據都很小,所以就會造成數據傾斜。
那麼要怎麼優化呢?
方法1:可以先找出這個user_id是什麼,過濾掉它,然後通過count(distinct user_id)計算出剩餘的那些user_id的個數,最後再加1(這1個就是那個被過濾掉的user_id,雖然它有大量的記錄,但是ser_id相同的都是同一個用戶,而我們要計算的就是用戶數)
sql語句展示:
分組求和後降序排序,就可以得到這個數據量最大的user_id是什麼,然後我們下一步操作時就過濾它,等計算完其他的再加上它這一個。
select user_id,count(user_id) from log group by user_id desc limit 2;
select count(distinct user_id)+1 as sum from log;
sum就是最終的結果--用戶數
方法2:我們可以先通過group by分組,然後再在分組得到的結果的基礎之上進行count
sql語句展示:
select count(*) from (select user_id from log group by user_id) new_log;

總的來說就是,數據傾斜的根源是key分布不均勻,所以應對方案要麼是從源頭解決(不讓數據分區,直接在map端搞定),要麼就是在分區時將這些集中卻無效的key過濾(清洗)掉,或者是想辦法將這些key打亂,讓它們進入到不同的rece中。

性能調優是指通過調整使得機器處理任務的速度更快,所花的時間更少,而數據傾斜的處理是hive性能調優的一部分,通過處理能夠大大減少任務的運行時間。

除了數據傾斜的處理之外,hive的優化還有其他方面的,例如where子句優化:
select * from a left outer join b on (a.key=b.key) where a.date='2017-07-11' and b.date='2017-07-11';
這是一個左外連接。
這個sql語句執行的結果是:得到的結果是表a與表b的連接表,且表中的記錄的date都是'2017-07-11'。
而這個sql語句的執行過程是:逐條獲取到a表的記錄,然後掃描b表,尋找欄位key值為a.key的記錄,找到後將b表的這條記錄連接到a表上,然後判斷連接後的這條記錄是否滿足條件a.date='2017-07-11' and b.date='2017-07-11',如果滿足,則顯示,否則,丟棄。
因為這是一個左外連接,且a為驅動表,連接時在a中發現key而在b中沒有發現與之相等的key時,b中的列將置為null,包括列date,一個不為null,一個為null,這樣後邊的where就沒有用了。
簡答的說這個方案的做法就是先按連接條件進行連接,連接後再看where條件,如果不滿足就丟棄,那之前連接所做的那些功夫就浪費了,白白耗費了資源(cpu等),增加了運行的總時間,如果有一種方案可以在未進行連接之前就直接判斷出不滿足最終的條件,那麼就可以直接丟棄它,這樣對於這樣的記錄就不要浪費資源以及時間去連接了,這樣也是能提升性能的,下面就看看這種方案:
sql語句:
將剛才的where限制條件直接放到on裡面,那麼就變成了滿足這三個條件才會進行連接,不滿足的直接過濾掉,就像上面所說的,少了無效連接那一步,就相對地節約了時間,如果這樣的無效連接的記錄很多的話,那麼採用這種改進版的方案無疑能夠較大程度地提高性能。
select * from a left outer join b on (a.key=b.key and a.date='2017-07-11' and b.date='2017-07-11');

不管怎麼說,我們在運行任務時,總是希望能加快運行速度,縮短運行時間,更快地得到結果,即提升性能,這是我們的目的,這就是我們所謂的性能調優。

關於小表join大表的補充:
表join時的操作是這樣的:
當操作到驅動表的某條記錄時,就會全局掃描另一張表,尋找滿足條件的記錄,而當掃描它時,為了讀取速度更快,一般都選先將它載入到內存,而內存的大小是有限的,為了不佔據過多的內存或者避免內存溢出,載入進入內存的表一般是小表,即數據量比較小,map join就是這樣做的。
即驅動表不放進內存,而另一張表(即要連接到驅動表的那一張表)就要先載入進內存,為了掃描速度更快,提高性能。
比如select * from a left outer join b on (a.key=b.key);
左外連接,驅動表是a,表b的記錄是要被連接到表a的,所以每在a上連接一條記錄就要被全局掃描一次的表是b,所以表b應先載入到內存(前提是表b是小表,如果是大表的話,估計會產生oom異常--out of memory內存溢出異常)。

select * from aa right outer join bb on (a.key=b.key);
右外連接,驅動表是bb,aa應先載入到內存(前提是小表)。

ps:希望我的分享能幫助到有需要的夥伴哦。我不是大神的哦,如果文中有誤,還請大家不吝賜教,幫忙指正,謝謝了!!!

H. Hive-3.1.2(五)解決數據傾斜問題和優化

再 hive 里輸入如下:

或在 hive-site.xml 里添加如下:

hive.groupby.skewindata=true:數據傾斜時負載均衡,當選項設定為true,生成的查詢計劃會有兩個MRJob:

第一個MRJob中,Map的輸出結果集合會隨機分布到Rece中,每個 Rece做部分聚合操作,並輸出結果,這樣處理的結果是相同的 GroupBy Key 有可能被分到不同的Rece中,從而達到負載均衡的目的;

第二個MRJob再根據預處理的數據結果按照GroupBy Key分布到Rece中(這個過程可以保證相同的GroupBy Key被分到統一個Rece中),最後完成最終的聚合操作。

由上面可以看出,起到至關重要的作用其實時第二個參數的設置,它使計算變成了兩個 MapRece,現在第一個中再 shuffle 過程 partition 時隨雞給 key 打標機,使每個 key 隨機均勻分布到各個 rece 上計算,但是這樣只能完成部分計算,因為相同key 沒有分配到相同 rece 上,所以需要第二次的 MapRece,這次就回歸正常 shuffle,但是數據分布不均勻的問題再第一次有很大的改善,因此基本解決數據傾斜。

再 hive 里輸入如下:

或在 hive-site.xml 里添加如下:

當使用以上參數設置時,需要注意sql寫的時候,小表一定要在前面(最左側)。

mapjoin 的主要意思就是,當連接兩個表是一個比較小的表和一個特大的表的時候,我們把比較小的table直接放到內存中去,然後再對比較大的表格進行map操作。join就發生在map操作的時候,每當掃描一個大的table中的數據,就要去查看小表的數據,哪條與之相符,繼而進行連接。這里的join並不會設計rece操作。map端join的優勢就是在於沒有shuffle。

還有種sql優化的方式,就是有條件的話,先對大表進行條件過濾,後對他join。

優化前

優化後

優化前的sql 會啟用一個 Rece 任務,優化後的會啟用兩個 Rece 任務。優化後的 Rece 必須設置 mapred.rece.tasks 數量才有效,否則默認還是1個。而count一定之啟用1個Rece。

日常統計場景中,我們經常會對一段十七內的欄位進行消重並統計數量,SQL語句類似於 優化前 的語句,這條語句是從一個表的符合where條件的記錄中統計不重復的id的總數。該語句轉化為 MapRece 任務後執行示意圖如下:

由於引入了 distinct,因此在Map階段無法利用 combine 對輸出結果消重,必須將id作為key輸出,在Rece階段再對來自於不同 Map 相同 key 的結果進行消重,計入最終統計值。

我們看到任務運行時Rece個數為1,對於統計大量數據量時,這會導致最終Map的全部輸出由單個Reuce處理,這唯一的Rece需要shuffle大量的數據,並且進行排序聚合等處理,這使得它稱為整個任務的IO和運算瓶頸。

經過上述分析後,我們可以嘗試顯示的增大 Rece 任務個數來提高Rece階段的並發,使每一個Rece的數據量控制在我們預想的范圍。

但這個調正不會影響 count 這種"全聚合(full aggregates)"的任務,它會忽略用戶指定的

我們利用hive對嵌套語句的支持,將原來一個 MapRece 任務轉為兩個任務,在第一階段選出全部的非重復id,在第二階段再對這些已消重的id進行統計。這樣在第一階段我們可以同過增大Rece的並發數,並發處理Map輸出。在第二階段,由於id已經消重,因此count(*) 操作Map階段不需要輸出原id數據,只輸出一個合並後的計數即可。這樣即使第二階段Hive強制指定一個Rece任務,極少量的Map輸出數據也不會使單一的Rece任務成為瓶頸,改進後的視圖如下:

Hive底層自動對小文件做了優化,用了 CombineTextInputFormat,將多個小文件切片合成一片,對應也就只會啟動一個 Map 任務。
合並完成的切片大小,如果 > mapred.max.split.size 的大小,就啟動一個新的切片任務。默認 mapred.max.split.size=134217728(128MB)

JVM 重用是hadoop調優參數內容,對hive的性能具有非常大的影響,特別是對於很難避免小文件的場景或者task特別多的場景,這類場景大多數執行事件都很短。這時jvm的啟動過程可能會造成相當大的開銷,尤其時執行的Job包含由成千上萬個task任務的情況。

JVM重用可以使得一個JVM進程在同一個JOB中重新使用N次後才會銷毀。

在hive裡面可以通過嚴格模式防止用戶執行那些可能產生意想不到的不好的效果的查詢,從而保護hive的集群。

用戶可以通過以下來設置嚴格模式,改成 unstrict 則為非嚴格模式。

在嚴格模式下,用戶在運行如下查詢的時候會報錯:

因為在測試環境下我們都把應用程序跑通了,如果還加上推測執行,如果有一個數據分片本來就會發生數據傾斜,執行事時間就是比其他的時間長,那麼hive就會把這個執行時間長的job當作運行失敗,繼而又產生一個相同的job去運行,後果可想而知,可以通過如下設置關閉推測執行機制。該機制默認時開啟的。

I. Hive數倉-數據傾斜優化

首先傾斜一般發生在rece階段

-- 解決方案

Hive默認啟動Map join 優化

如果是查詢的語句列如(group, join)可以直接在yarn kill掉
如果是 insert ,,, select 就要權衡 等他跑完

結束後 對sql語句發生數據傾斜的地方進行定位、給出解決方案、測試、變更

可以要對進行join 或者group by 的欄位采樣
列如 訂單表1000w條數據 商品表100w條數據
a.pid = b.pid

select pid ,count(*) from tb_order group by pid;
查看一下訂單表中 商品key的個數
列如商品1 900w條 那麼這個數據一定會發生數據傾斜

textfile 文本文件 hdfs 默認存儲
SequenceFile 二進制文件 使用二進制位元組碼存儲 存儲方式為行存儲,其具有使用方便、可分割、
可壓縮的特點
RCfile 、
ORCfile 列式存儲
行式存儲 方便插入 列式存儲 方便查詢
:相比TEXTFILE和SEQUENCEFILE,RCFILE由於列式存儲方式,數據載入時性能消耗較大,但是具有較好
的壓縮比和查詢響應。
數據倉庫的特點是一次寫入、多次讀取,因此,整體來看,RCFILE相比其餘兩種格式具有較明顯的優勢