MapReduce Design Patterns(chapter 2 (part 3))(四)

Inverted Index Summarizations

Pattern Description

反向索引模式在MapReduce分析中常常做爲一個例子。咱們將會討論咱們要建立的term跟標識符之間映射的通常狀況。html

 

Intent

根據數據集生成索引,用於快速搜索或數據的富集能力。web

Motivation

根據關鍵詞索引大數據很是方便,搜索能追蹤term找到包含指定值的記錄。建立索引須要以前進行額外的處理,花時間去作這項工做能有效減小咱們尋找東西的時間。數據庫

 

搜索引擎爲了提升搜索性能建立索引。設想鍵入一個關鍵詞,讓引擎去互聯網抓取信息並建立一個頁面的列表返回給你。這樣的查詢會耗費巨大的時間。若是建立一個反向索引,搜索引擎會在以前就知道跟關鍵詞相關的互聯網頁面,結果很簡單的展示給用戶。這種索引也常常注入數據庫中,爲了更快的查詢響應。使用MapReduce建立反向索引是相對簡單的任務,由於框架處理絕大多數工做。json

Applicability

反向索引用在搜索查詢時須要快速響應的狀況下。查詢的結果能被預先處理並放入數據庫。數據結構

Structure

圖2-5展現了MapReduce中執行反向索引的組織結構。下面詳細介紹MapReduce組件各部分的功能:app

 

·mapper輸出須要索引的字段做爲key,惟一標識符做爲value。負載均衡

·若是使用identity reducer,能夠不用combiner,由於這種狀況下combiner僅僅執行一些不須要的處理。不少實如今輸出到文件系統以前將組和值關聯起來。這種狀況下,combiner就能使用。這裏對字節計數沒有有益的影響,不像其它模式裏的combiner,但也會有一種改進。框架

·partitioner負責決定含有相同key的值可以拷貝到同一個reducer中。若是中間鍵不是平均分發的,爲了更有效的負載均衡,能夠從新定義partitioner。ide

·reducer將接收一系列惟一的記錄標識跟輸入key關聯。標識符能夠是一些惟一的分隔符鏈接起來,使輸出爲每組一個鍵值對,也能夠是輸入key裏的value,正如identity reducer。oop

Figure 2-5. The structure of the inverted index pattern

 

Consequences

最終的輸出是包含字段到一系列包含相關字段值的記錄的標識符的映射的分片文件的集合。

Performance analysis

建立反向索引的性能主要取決於mapper中解析內容的計算代價,索引鍵的基數,和每一個key中內容標識符的數量。

 

在Mapper中解析文本或其餘類型的內容,有時是計算緊張的操做。這個問題對半結構化的數據特別突出,例如xml或json,由於這種類型可能須要解析任意的信息量,到一個可用的對象中。儘量高效的解析傳入的記錄來提升整個job的性能,是很是重要的。

 

若是惟一鍵的數量和標識符的數量很是巨大,將會發送到reducer更多的數據。越多的數據發送到reducer,你應該增長reducer的數量來提升reduce階段的並行度。

 

反向索引對索引鍵中的熱點很是敏感,由於索引鍵不多是均勻分佈的。例如,reducer在文本搜索中處理單詞」the」,將會變得很是繁忙,由於文本中有大量的單詞「the」。這將由於個別執行時間較長的reducers而影響到整個job的執行。爲了不這個問題,你可能須要實現一個自定義的partitioner,或忽略這個關鍵詞,不給值。

Inverted Index Example

Wikipedia reference inverted index

建立反向索引對MapReduce來講是一項簡單的工做,它常常做爲初學者繼word count以後的第二個例子。與word count很像,大多數的工做由MapReduce框架來作。

 

假設要在每一個引用到stackOverflow評論的wikipedia頁面添加stackoverflow的連接。下面的例子分析每個stackOverflow的評論來找出是否從wikipedia連接過來的。若是是,連接跟評論的id一同輸出,生成反向索引。到reduce階段時,引用到相同連接的評論id會分組到一塊兒。而後經過空格分隔符把分組鏈接起來,輸出到文件系統。至此,能夠用這些數據文件中引用到wikipedia的全部評論更新wikipedia頁面。

 

問題:給出用戶評論數據,在一系列回答問題的id上建立wikipedia的反向索引

 

Mapper code。Mapper解析stackOverflow帖子數據輸出wikipedia url和回帖記錄的id。從xml屬性中抽取內容,提交類型(發帖or回帖),記錄id。若是提交類型是提問,不是回答,標識爲「2」(代碼中明明寫的是1,個人鈦合金狗眼。。。)(本人認爲有誤),而後解析內容,找到wikipedia url。使用getWikipediaURL方法,傳入非轉義的html文本,找到就返回,不然返回空。這段代碼就不在這裏列出了。若是url找到,就把它當作key,記錄id做爲值一併輸出。

 

public static classWikipediaExtractor extends

Mapper<Object,Text, Text, Text> {

privateText link =new Text();

privateText outkey =new Text();

publicvoid map(Object key,Text value,Context context)

throwsIOException,InterruptedException {

Map<String,String>parsed = MRDPUtils.transformXmlToMap(value

.toString());

// Grab the necessary XML attributes

String txt= parsed.get("Body");

String posttype= parsed.get("PostTypeId");

String row_id= parsed.get("Id");

// if the body is null, or the post is a question (1), skip

if(txt== null|| (posttype!= null&&posttype.equals("1"))) {

return;

}

// Unescape the HTML because the SO data is escaped.

txt= StringEscapeUtils.unescapeHtml(txt.toLowerCase());

link.set(getWikipediaURL(txt));

outkey.set(row_id);

context.write(link,outkey);

}

}

 

Reduce code。Reducer會迭代全部值,以string類型,空格爲分隔符,把記錄id鏈接起來。輸入key做爲輸出key,value是鏈接起來的string。

 

public static classConcatenator extendsReducer<Text,Text,Text,Text> {

privateText result =new Text();

publicvoid reduce(Text key,Iterable<Text>values, Context context)

throwsIOException,InterruptedException {

StringBuilder sb= newStringBuilder();

booleanfirst =true;

for(Text id: values) {

if(first) {

first= false;

}else {

sb.append(" ");

}

sb.append(id.toString());

}

result.set(sb.toString());

context.write(key,result);

}

}

 

Combiner optimization。Combiner能夠在reduce階段以前作一些預鏈接。由於全部的記錄id簡單鏈接在一塊兒,須要拷貝到reducer的字節量比數值聚合模式中的要多。Reduce代碼能夠用做combiner。

 

Counting with Counters

Pattern Description

這種模式利用了MapReduce框架自己的計數器功能在map端作全局的計算,不作任何輸出。

Intent

一種獲取大數據量下整體計數值得有效手段。

Motivation

一個計數或總和能告訴你數據某個字段的信息,或整個數據信息。根據每一個小時的計數值就能獲得有用的直方圖。這種模式也能夠用相似word count的程序計算:這種狀況下,對每一個輸入記錄,輸出相同記錄做爲key,表示這一小時處理了這條記錄,並給計數1。惟一的reduce會對全部輸入值求和,輸出這一小時內最終的記錄條數。這種使用起來很簡單,但用計數器會更高效。不會寫任何的鍵值對,只利用框架的計數機制跟蹤輸入的記錄條數。這樣就不須要reduce階段並不須要求和。框架會監控計數器的名字和它們相關的值,並根據全部tasks聚合這些值,包括任何失敗的task attempts。

 

例如你想找到天天你的員工大量訪問站點的次數。假設你有若干員工,能夠對web日誌解析時用條件過濾。不用輸出員工的姓名和計數1,你能夠建立一個計數器,包含員工id,自增1。在job的最後,簡單的從框架獲取計數器並保存到任何你想要的的地方。

 

許多計數器是內建在框架裏的,例如,輸入輸出記錄數和字節數。Hadoop容許程序猿自定義任何可能須要的計數器。這種模式描述了怎樣利用這種自定義計數器從數據集收集計數或合計指標。使用計數器的主要好處就是全部的計數都在map階段完成。

 

Notice:使用計數器須要清楚的是它們都存儲在jobTracker的內存裏。Map任務序列化它們,連同更新狀態被髮送。爲了運行正常且jobTracker不會出問題,計數器的數量應該在10-100個,計數器不只僅只用來聚合MapReduce job的統計值。新版本的hadoop限制了計數器的數量,以防給jobTracker帶來損害。你最不想看到的事情就是因爲定義上百個計數器而使jobTracker宕機。

 

Applicability

用計數器計數適用的狀況:

·在大數據集上收集計數或求和。

·建立的計數器個數較小,兩位數之內。

Structure

圖2-6展現了這種模式的組織結構。

·mapper每次處理每條輸入記錄根據某一條件自增計數器。計數器能夠是自增1的計數,也能夠是自增某數值的求和計算。這些計數器在TaskTracker聚合之後加到jobTracker上,直到job結束。失敗任務的計數器在jobTracker最終求和時不會計算在內。

·由於job只有map,因此沒有combiner,partitioner,reducer。

Consequences

最終的輸出是從job框架獲取的計數器的集合。分析自己沒有實際的輸出。可是job須要一個輸出目錄。這個目錄將會產生幾個空文件風別對應幾個map任務。Job完成時目錄應該被刪掉。

 

Known uses

統計記錄的條數:

簡單的統計給定時間段內記錄條數是很常見的,這是框架提供的典型的計數器。

統計數量較小的惟一事件:

計數器也能夠在程序運行中建立,用字符串變量。你可能如今就知道值是什麼,但計數器沒必要提早建立。簡單的使用字段值建立一個計數器並自增,足夠解決這類問題。只須要保證計數器數量要小。

求和:

計數器能用來作數據字段的求和。但不是在reduce端執行求和,僅僅建立並使用它求字段值的和。

 

Figure 2-6. The structure of the counting with counters pattern

Performance analysis

使用計數器能很快的完成計算,由於數據僅僅在map中處理,沒有輸出要寫。性能主要取決於執行的map的個數和處理每條記錄花費的時間。

Counting with Counters Example

Number of users per state

對於這個例子,咱們只用map來統計每一個州下用戶的數量。位置屬性是用戶鍵入的值,不須要任何具體的輸入。因爲此,會存在大量的空字段,還有編造的位置數據。咱們須要處理這個問題,處理每條輸入記錄時也要保證不要建立太多的計數器。咱們建立計數器以前要檢驗位置數據是否包含狀態縮寫碼。這樣建立最多52個計數器—50個州(美帝),2個null或empty。這對於jobTracker來講是很容易管理的計數器的數量,你的程序不能比這個多不少。

 

問題:用hadoop自定義計數器統計每一個州的用戶數。

 

Mapper code。Mapper讀取每條記錄並獲得這個用戶的位置。位置是用空殼分隔的,用表明州的信息搜索。咱們把全部州的縮寫放進內存,來防止過多的計數器被建立。位置數據僅僅是用戶設置的字符串,並非其餘數據結構。若是州被識別出來,計數器遞增1。計數器經過組和名字標識。這裏,組是州,一個公共的string類型變量,名字是州的縮寫代碼。

 

public static classCountNumUsersByStateMapper extends

Mapper<Object,Text, NullWritable, NullWritable> {

public static finalString STATE_COUNTER_GROUP ="State";

public static finalString UNKNOWN_COUNTER ="Unknown";

public static finalString NULL_OR_EMPTY_COUNTER ="Null or Empty";

privateString[]statesArray =new String[] {"AL", "AK", "AZ","AR",

"CA","CO", "CT", "DE","FL", "GA", "HI","ID", "IL", "IN",

"IA","KS", "KY", "LA","ME", "MD", "MA","MI", "MN", "MS",

"MO","MT", "NE", "NV","NH", "NJ", "NM","NY", "NC", "ND",

"OH","OK", "OR", "PA","RI", "SC", "SF","TN", "TX", "UT",

"VT","VA", "WA", "WV","WI", "WY" };

privateHashSet<String>states = new HashSet<String>(

Arrays.asList(statesArray));

publicvoid map(Object key,Text value,Context context)

throwsIOException,InterruptedException {

Map<String,String>parsed = MRDPUtils.transformXmlToMap(value

.toString());

// Get the value for the Location attribute

String location= parsed.get("Location");

// Look for a state abbreviation code if the

// location is not null or empty

if(location!= null&& !location.isEmpty()) {

// Make location uppercase and split on white space

String[]tokens = location.toUpperCase().split("\\s");

// For each token

booleanunknown =true;

for(String state: tokens) {

// Check if it is a state

if(states.contains(state)) {

// If so, increment the state's counter by 1

// and flag it as not unknown

context.getCounter(STATE_COUNTER_GROUP,state)

.increment(1);

unknown= false;

break;

}

}

// If the state is unknown, increment the UNKNOWN_COUNTER counter

if(unknown) {

context.getCounter(STATE_COUNTER_GROUP,UNKNOWN_COUNTER)

.increment(1);

}

}else {

// If it is empty or null, increment the

// NULL_OR_EMPTY_COUNTER counter by 1

context.getCounter(STATE_COUNTER_GROUP,

NULL_OR_EMPTY_COUNTER).increment(1);

}

}

}

 

Driver code。驅動代碼大部分都是樣板,不用動,這個例子要改一些:job完成後獲取計數器。Job成功之後就把結果打印到標準輸出。這些計數器的值也會在job完成後輸出的指定目錄,因此把他們寫到標準輸出多是多餘的,若是你會經過查看日誌文件獲取他們。輸出目錄隨後被刪掉,無論成功與否,由於這樣的job建立的不是有形產出,基本沒意義。

 

...

intcode =job.waitForCompletion(true) ?0 : 1;

if(code== 0) {

for(Counter counter: job.getCounters().getGroup(

CountNumUsersByStateMapper.STATE_COUNTER_GROUP)) {

System.out.println(counter.getDisplayName() + "\t"

+counter.getValue());

}

}

// Clean up empty output directory

FileSystem.get(conf).delete(outputDir,true);

System.exit(code);

 

摘錄地址:http://blog.csdn.net/cuirong1986/article/details/8456923

相關文章
相關標籤/搜索