MapReduce優化----優化方案着手點

Hadoop/MapReduce 優化方案
從三個方面着手優化 :
1. hadoop配置
2. 設計mapred/job
3. 代碼級別.
4. 改造hadoop
一. conf/hadoop-site.xml配置.
經驗要求高, 特別需要結合實際情況.
典型參數如
複製因子,
mapred.child.java.opts,
mapred.tasktracker.map.tasks.maximum,
mapred.tasktracker.reduce.tasks.maximum,
mapred.map.tasks,
mapred.reduce.tasks,
fs.inmemory.size.mb,
dfs.block.size
等等
二. 在同一個job內完成儘可能多的計算任務, 主要是設計key和自定義OutputFormat, 將能合併的計算任務合併.
舉例 : 用戶訪問行爲(userid, ip, cookie), 分別統計每個用戶的ip數和cookie數.
最簡單的設計, 是使用量個job, 分別計算ip數和cookie數.但是我們可以按照下面的思路, 在一個job中完成這兩項計算 :
(a). 把userid和字段存儲到key中
public class UserKey implements WritableComparable<UserKey>{
    int userId;//userid
    byte field;//0 代表ip, 1代表cookie
    @Override
    public int compareTo(UserKey o) {
        if(userId > o.userId)return 1;
        if(userId < o.userId)return -1;
        if(field > o.field)return 1;
        if(field < o.field)return -1;
        return 0;
    }
    @Override
    public void readFields(DataInput in) throws IOException {
    // TODO Auto-generated method stub
    }
    @Override
    public void write(DataOutput out) throws IOException {
    // TODO Auto-generated method stub
    }
}
(b). 實現自定義的OutputFormat, 下面是兩處關鍵代碼如下 :
(x).
SequenceFile.Writer[] writers = new SequenceFile.Writer[2];
writers[0] = SequenceFile.createWriter(FileSystem.get(conf), conf, "ip", IntWritable.class, IntWritable.class, CompressionType.BLOCK, new DefaultCodec());
writers[1] = SequenceFile.createWriter(FileSystem.get(conf), conf, "field", IntWritable.class, IntWritable.class, CompressionType.BLOCK, new DefaultCodec());
(xx).
writers[key.field].append(key.userId, value.get());
三. 避免不必要的reduce任務.
(1). 假定要處理的數據是排序且已經分區的. 或者對於一份數據, 需要多次處理, 可以先排序分區.
(2). 自定義InputSplit, 將單個分區作爲單個mapred的輸入.
(3). 在map中處理數據, Reducer設置爲空.
這樣, 既重用了已有的 "排序", 也避免了多餘的reduce任務.
四. 使用自定義的MapRunnable.
hadoop自帶了兩個MapRunnable,
(1). 一個是默認的單線程MapRunnable, org.apache.hadoop.mapred.MapRunner
(2).另一個是多線程的, org.apache.hadoop.mapred.lib.MultithreadedMapRunner.
根據特定情況, 可以自定義MapRunnable,
(1). 啓用多線程, 比如web爬行時, 可啓用多線程抓取網頁.
(2). 避免map時, 單臺tasktracker上輔助數據冗餘, 比如在多模匹配時, 避免生成多份DFA.
五. 在某些情況下, 利用數據分佈特性設計PARTITIONER的分區算法, 避免單個mapred消耗時間過長.
這跟木桶原理有些神似.
比如處理大量字符串時,
(1). 已知首字不同的字符串之間不存在任何關聯關係
(2). 原始數據在某些 "首字" 上分佈密集, 另一些 "首字" 上分佈稀疏.
例如, 原始數據中, 1億個以3開頭, 1億個以7開頭, 3個以6開頭.
那麼,
(1). 如果以首字對4求餘分區, 則 "1億個以3開頭" 和 "1億個以7開頭"將落在同一分區.若hadoop羣集只支持同時2個map任務, 則...
(2). 如果以首字對3求餘分區, 則 "1億個以3開頭" 和 "1億個以7開頭"將落在不同分區.
六. 最大限度地重用對象, 避免對象的生成/銷燬開銷.
該點在hadoop自帶的 org.apache.hadoop.mapred.MapRunner中尤爲突出,
它使用同一個key對象和同一個value對象不停讀取原始數據, 再將自身交給mapper處理.
(此處注意, 若要保留該對象的即時狀態, 需要clone, 深克隆或淺克隆.)
七. 在邏輯意義上, 合併同一對象. 如dotnet和java中的字符串INTERN技術.
八. 根據已有條件, 簡化循環判定.
比如, for(int i = 0; i < end && i < size; i++);
可以改成 :
end = end < size ? end : size;
for(int i = 0; i < end; i++);
九. 降低多線程數目, 而讓固定數目的線程循環處理.
比如, 一臺機器8個CPU, 現在需要處理80億個數據,
那麼下面兩個方案 :
(1). 啓動800個線程, 每個線程處理80億/800個數據.
(2). 啓動8個線程(注意, 此處是8個), 每個線程循環處理, 每次循環處理100萬個.
通常我個人選擇方案(2).因爲 :
(1). 最大限度利用了CPU.
(2). 避免了線程調度.
(3). 在java中, 可以使用AtomicInteger控制線程循環, AtomicInteger的效率很高.
(4). 有時, 還可以避免單個線程消耗時間過長.
十. 使用位移替代浮點數計算. 比如用 100 >> 3替代100 * 0.125.
(另外, 我們會需要將某個中間值乘以一個調節因子(經驗值), 比如乘以0.12,
如果乘以0.12和0.124 "差不多" 時, 可以考慮直接使用位移).
十一. 避免循環體內不必要的判斷邏輯, 與第八條不同.
比如, 處理10億個數據, 每遇到一個有效數據時, 需要同前一個有效數據進行關聯處理(或與前一箇中間值進行關聯處理),
for(int i = 0; i < size; i++)
{
//1. 判定是否存在前一個有效數據
//2. 如果不存在前一個有效數據, 則continue;
//3. 如果存在前一個有效數據, 則進行關聯處理, 再continue.
}
通常在此種需求下, 一旦遇到一個有效數據, 必定會產生一個可供後續緊鄰數據關聯的值,
那麼 :
int i = 0;
for(int i = 0; i < size; i++)
{
//1. data[i]是否有效?
//2. data[i]無效, continue;
//3. data[i]有效, break;
}
for(; i < size; i++)
{
//與前一個有效數據進行關聯處理, 再continue.
}
十二. 方法調用過程, 輔助數據儘量放在方法體內, 避免使用全局輔助數據, 一來節省內存, 二來提高對象可重用性.
十三. 儘量不要生成轉瞬即逝的對象, 或者專門構建專屬對像來完成這一任務.
比如 :
1). 提供直接使用構造函數參數進行序列化的靜態方法, 避免先使用參數構造對象再進行序列化.
2). 參考上述第六點.
十四. 利用-1 和 1的關聯性, 減少內存使用量, 或攜帶更多的信息.
比如java.util.Arrays.binarySearch方法的返回值.
十五. 對於方方正正的多位數組Arr[d0][d1][d2]..[dn], 且di >> d(i+1)時, 可以考慮使用一維數組替代, 減少對象.
這是因爲java中多位數組實際上使用 "數組的數組" 實現的.
十六. 儘量使key的WritableComparable性能最佳, 儘量使value的Writable性能最佳.
比如使用掩碼操作.
十七. 儘早丟棄無關對象.
見 "使用hadoop/mapred的典型計數問題".
十八. 改造hadoop, 使merge過程更具彈性, 或更符合實際需求.
比如 :
1). 使reduce的<key, values>中的values按照順序迭代.
2). 見 "使用hadoop/mapred的典型計數問題".
十九. 有效設計mapred中的combiner, 儘早降低I/O等操作.
此過程中, 可以結合自定義OutputFormat, 使得同一個Recuder類可同時充當map->merge->reduce中的後兩個過程.
見 "使用hadoop/mapred的典型計數問題".
發佈了119 篇原創文章 · 獲贊 11 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章