來自Twitter的Hadoop優(yōu)化經(jīng)驗(yàn):集群越大越明顯!
一、來源
Streaming Hadoop Performance Optimization at Scale, Lessons Learned at Twitter
二、讀后感
2.1 概要
此稿介紹了Twitter的核心數(shù)據(jù)類庫團(tuán)隊(duì),在使用Hadoop處理離線任務(wù)時(shí),使用的性能分析方法,及由此發(fā)現(xiàn)的問題和優(yōu)化手段,對如何使用 JVM/HotSpot profile(-Xprof)分析Hadoop Job的方法調(diào)用開銷、Hadoop配置對象的高開銷、MapReduce階段的排序中對象序列化/反序列的高開銷問題及優(yōu)化等給出了實(shí)際可操作的方案。
其介紹了Apache Parquet這一面向列的存儲格式,并成功應(yīng)用于列投影(column project),配合predicated push-down技術(shù),過濾不需要的列,極大提高了數(shù)據(jù)壓縮比和序列化/反序列化的性能。
純干貨。
32個(gè)贊!
2.2 優(yōu)化總結(jié)
1) Profile!(-Xprofile)性能優(yōu)化不能靠猜,而應(yīng)靠分析!
2) 序列化開銷很大,但是Hadoop里有許多序列化(操作)!
3) 根據(jù)特定(數(shù)據(jù))訪問模式,選擇不同的存儲格式(面向行還是面向列)!
4) 使用column projection。
5) 在Hadoop的MR階段,排序開銷很大,使用Raw Comparators以降低開銷。
注:此排序針對如Comparator,其會引發(fā)序列化/反序列化操作。
6) I/O并不一定就是瓶頸。必要的時(shí)候要多I/O換取更少的CPU計(jì)算。
JVM/HotSpot原生profile能力(-Xprof),其優(yōu)點(diǎn)如下:
1) 低開銷(使用Stack sampling)。
2) 能揭示開銷***的方法調(diào)用。
3) 使用標(biāo)準(zhǔn)輸出(Stdout)將結(jié)果直接寫入Task Logs。
2.3 Hadoop的配置對象
1) Hadoop的Configuration Object開銷出人意料的高。
2) Conf的操作看起來就像一個(gè)HashMap的操作。
3) 構(gòu)造函數(shù):讀取+解壓+分析一個(gè)來自磁盤的XML文件
4) get()調(diào)用引起正則表達(dá)式計(jì)算,變量替換。
5) 如果在循環(huán)中對上述等方法進(jìn)行調(diào)用,或者每秒一次調(diào)用,開銷很高.某些(Hadoop)Jobs有30%的時(shí)間花在配置相關(guān)的方法上!(的確是出人意料的高開銷)
總之,沒有profile(-Xprof)技術(shù),不可能獲取以上洞察,也不可能輕易找到優(yōu)化的契機(jī)和方向,需要使用profile技術(shù)來獲知I/O和CPU誰才是真正的瓶頸。
#p#
2.4 中間結(jié)果的壓縮
- Xprof揭示了spill線程中的壓縮和解壓縮操作消耗了大量時(shí)間。
- 中間結(jié)果是臨時(shí)的。
- 使用lz4方法取代lzo level 3,減少了30%多的中間數(shù)據(jù),使其能被更快地讀取。
- 并使得某些大型Jobs提速150%。
2.5 對記錄的序列化和反序列,會成為Hadoop Job中開銷***的操作!
2.6 對記錄的序列化是CPU敏感的,相對比之下,I/O都不算什么了!
2.7 如何消除或者減小序列化/反序列化引起的(CPU)開銷?
2.7.1 使用Hadoop的Raw Comparator API(來比較元素大小)
開銷分析:如下圖所示,Hadoop的MR在map和reduce階段,會反序列化map結(jié)果的keys以在此階段進(jìn)行排序。
(反序列化操作)開銷很大,特別是對于復(fù)雜的、非原語的keys,而這些keys又很常用。
Hadoop提供了一個(gè)RawComparator API,用于對已序列化的(原始的)數(shù)據(jù)(字節(jié)級)進(jìn)行比較:
不幸的是,需要親手實(shí)現(xiàn)一個(gè)自定義的Comparator。
現(xiàn)在,假設(shè)數(shù)據(jù)已序列化后的字節(jié)流,本身是易于比較的:
Scala有個(gè)很拉風(fēng)的API,Scala還有一些宏可以產(chǎn)生這些API,以用于:
Tuples , case classes , thrift objects , primitives , Strings,等等數(shù)據(jù)結(jié)構(gòu)。
怎么拉風(fēng)法呢?首先,定義一個(gè)密集且易于比較的數(shù)據(jù)序列化(字節(jié))格式:
其次,生成一個(gè)用于比較的方法,以利用這種數(shù)據(jù)格式的優(yōu)勢:
下圖是采用上述優(yōu)化手段后的比較開銷對比:
提速到150%!
接著優(yōu)化!
#p#
2.7.2 使用column projection
不要讀取不需要的列:
1) 可使用Apache Parquet(列式文件格式)。
2) 使用特別的反序列化手段可以在面向行的存儲中跳過一些不需要的字段。
面向列的存儲中,一整列按順序存儲(而不是向面向行的存儲那樣,列是分開存儲的):
可以看到,面向列的存儲,使得同類型的字段被順序排在一起(易于壓縮):
采用Lzo + Parquet,文件小了2倍多!
2.7.3 Apache Parquet
1) 按列存儲,可以有效地進(jìn)行列投影(column projection)。
2) 可按需從磁盤上讀取列。
3) 更重要的是:可以只反序列化需要的列!
看下效果:
可以看到,列數(shù)越少,Parquet的威力越大,到40列時(shí),其效率反而不如Lzo Thrift。
- 在讀取所有列的情況下,Parquet一般比面向行的存儲慢。
- Parquet是種密集格式,其讀性能和模式中列的數(shù)目相關(guān),空值讀取也消耗時(shí)間。
- 而面向行的格式(thrift)是稀疏的,所以其讀性能和數(shù)據(jù)的列數(shù)相關(guān),空值讀取是不消耗時(shí)間的。
#p#
跳過不需要的字段,如下所示:
- 雖然,沒有降低I/O開銷
- 但是,可以僅將感興趣的字段編碼進(jìn)對象中
- 相對于從磁盤讀取 + 略過編碼后字節(jié)的開銷,在解碼字符串時(shí)所花的CPU時(shí)間要高的多!
看下各種列映射方案的對比:
Parquet Thrift還有很多優(yōu)化空間;Parquet在選取的列數(shù)小于13列之前,是更快的;此模式相對平坦,且大多數(shù)列都被生成了。
- 還可以采用Predicate Push-Down策略,使得Parquet可以跳過一些不滿足過濾條件的數(shù)據(jù)記錄。
- Parquet存儲了一些統(tǒng)計(jì)信息,比如記錄的chunks,所以在某些場景下,可以通過對這些統(tǒng)計(jì)信息進(jìn)行讀取分析,以跳過整個(gè)數(shù)據(jù)塊(chunk)。
注:左圖為column projection,中圖為predicate push-down過濾,右圖為組合效果。可以看到很多字段被跳過了,那絕壁可以優(yōu)化序列化/反序列化的效率
下圖則展示了push-down過濾 + parquet的優(yōu)化成效:
2.8 結(jié)語
感嘆:Twitter真是一家偉大的公司!
上述優(yōu)化手段,集群越大、Hadoop Job越多,效果越明顯!













































