基於MongoDB MapReduce的統計分析

前面已經簡單介紹了MongoDB在OECP社區的一個應用:動態消息的設計實現。在上次的應用中,我們只介紹了MongoDB最基本的查詢的功能,今天我再介紹一下MongoDB更加高級的應用:用MongoDB做統計分析。
OECP社區中,我們爲了更加準確的分析網站的訪問情況,以便能夠爲用戶更準確的推薦他們感興趣的內容,我們需要將頁面的訪問記錄存儲下來。對於這些數據,主要由以下幾個特點:
 

  • 與業務無關,儘量將數據存儲和業務數據分離,減少業務數據庫的壓力。而且對數據的一致性要求不高。
  • 每當訪問一個頁面就要存儲一條記錄,實時插入操作的要求很高,當然可以使用緩存作爲臨時緩衝來解決數據頻繁更新的問題。
  • 數據隨着訪問量的增長膨脹的很快,如果一個頁面1天有100個PageViews,將會新增100條數據,數據量遠遠高於業務數據,而且要比我們上次說的消息動態的數據的數量級要大得多。網站要儘量存儲至少兩個月的數據,當網站訪問量很大的時候,要解決的是海量數據的存儲。

所以從存儲上考慮,我們依然選擇了MongoDB作爲持久存儲。由於NoSQL數據庫在數據查詢的多樣性能力太低,特別是標準的Key-Value數據庫,一般的做法就是用NoSQL負責日誌的存儲,分析需要將數據抽取到關係數據庫中再進行統計查詢。但是MongoDB卻提供給我們非常豐富的查詢統計功能,group 和MapReduce都能實現SQL中group by,sum,count之類的統計查詢分析。Group的功能已經可以實現簡單的統計功能,但是當數據量非常大的時候,group處理能力就不太好了,所以我們一開始就使用MapReduce進行統計分析。
先看一下官方對MapReduce的介紹:
db.runCommand(
 { mapreduce : <collection>,
   map : <mapfunction>,
   reduce : <reducefunction>
   [, query : <query filter object>]
   [, sort : <sort the query.  useful foroptimization>]
   [, limit : <number of objects to returnfrom collection>]
   [, out : <output-collection name>]
   [, keeptemp: <true|false>]
   [, finalize : <finalizefunction>]
   [, scope : <object where fields go into javascript global scope >]
   [, verbose : true]
 }
);
而java驅動下提供的方法主要有兩個:
DBCollection.mapReduce(String map, String reduce,String outputCollection,
                  DBObject query);
DBCollection.mapReduce(DBObject command);//該接口按照上面的介紹,總是報錯,不知道此該如何應用
 
PV數據存儲結構:(這些屬性主要是爲了支持我們以後根據各種維度去分析)
entityId:實體ID,
entityName:實體名稱,
userid:(登錄)訪問者ID,
sessionId:會話ID,
referer:來源URL,
url:當前頁面url,
title:顯示的標題,
date:訪問時間,
ip:訪問者IP
 
第一個應用場景:當訪問某用戶的空間時,得到某用戶最新的訪問記錄,同一個頁面重複訪問的話,返回最新的一次訪問。
 

  • 首先是map方法,主要是定義outputCollection的結構。OutputCollection的輸出結構爲:{_id:key,value:value}
java 代碼
  1. String mapfun = "function(){emit({url:this.url,title:this.title},this.date)}";//key={url:this.url,title:this.title},value=reduce方法的返回值。   
  • 其次是reduce方法
java 代碼
  1. String reducefun = "function(key,vals){var date=0; for(var i in vals){ if(date==0){date=vals[i];}else if(vals[i]>date){date=vals[i];}} return date;}";//如果同一個key的數據,相互比較時間,將最近時間返回。   
  • 執行
java 代碼
  1. DBObject query = newBasicDBObject();   
  2. query.put("userid", userid);   
  3. query.put("date", newBasicDBObject("$gte", fromDate));   
  4. *.getCollection().mapReduce(mapfun, reducefun,"pageview_results", query);//最好定義query,以降低統計的原始結果集   
  • 遍歷pageview_results集合的結果:[{_id:{url:”/blog/yongtree/258”,title:’博客1’},value:’2010-10-11 20:30:56’},{_id:{url:”/blog/slx/288”,title:’博客2’}, value:’2010-10-01 02:23:33’}]

 
注意:mapfun和reducefun字符串裏面是寫的javascript的方法,MongoDB可以在服務器端進行js的解析。如果這個方法寫的不對,程序將不能正常執行。
 
第二個應用場景:當訪問某個具體的內容時,返回某段時間曾經瀏覽過這篇文章的其他人關注的其他內容,以便對當前用戶有一個內容的引導。
 

  • 首先先找出某段時間內曾經訪問該內容的人作爲統計的條件,我們使用sessionId而不是userid,是爲了將沒有登錄的用戶的訪問算進來一起統計
java 代碼
  1. DBObject query = newBasicDBObject();   
  2.     query.put("entityId", entityId);   
  3.     query.put("entityName", entityName);   
  4.     query.put("date", newBasicDBObject("$gte", fromDate));   
  5.     query.put("date", newBasicDBObject("$lt", toDate));   
  6. List sessionIds = this.mongoService.getCollection().distinct("sessionId", query);//這裏運用了取出結果集中的重複值的函數distinct(String key,DBObject query),相當於SQL:select distinct(name) from table   
  • 定義map方法,主要是定義outputCollection的結構。OutputCollection的輸出結構爲:{_id:key,value:次數瀏覽的次數}
java 代碼
  1. String mapfun = " function(){emit({url:this.url,title:this.title},1)}";//key={url:this.url,title:this.title},value=reduce方法的返回值。以爲是計算數據的次數,所以這裏的value定義的是常量1   
  • 定義reduce方法
java 代碼
  1. String reducefun = " function(key,vals){var count=0; for(var i in vals){count+=vals[i];} return count;}";//如果同一個key的數據出現的次數進行求和。   
  • 執行
java 代碼
  1. *.getCollection().mapReduce(mapfun, reducefun,"pageview_results"new BasicDBObject("sessionId",new BasicDBObject("$in",sessionIds.toArray())));   
  • 遍歷pageview_results集合的結果:[{_id:{url:”/blog/yongtree/258”,title:’博客1’},value:’45.0’},{_id:{url:”http://www.po-soft.com/blog/slx/288”,title:’博客2’}, value:’30.0’}]

 
前臺展現的效果:
 

  • 當進入用戶空間時,展現空間主人關心的其他內容


 

  • 當瀏覽某篇內容時,展現其他瀏覽者的關心的內容


 
繼續關注OECP社區,我們將會實踐和發佈更多基於MongoDB的應用。本着共享的精神,該文檔可以被轉載和應用,但是要註明出處。
作者主頁:http://www.po-soft.com/hi/yongtree
 

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