前面已經簡單介紹了MongoDB在OECP社區的一個應用:動態消息的設計實現。在上次的應用中,咱們只介紹了MongoDB最基本的查詢的功能,今天我再介紹一下MongoDB更加高級的應用:用MongoDB作統計分析。
OECP社區中,咱們爲了更加準確的分析網站的訪問狀況,以便可以爲用戶更準確的推薦他們感興趣的內容,咱們須要將頁面的訪問記錄存儲下來。對於這些數據,主要由如下幾個特色:
javascript
- 與業務無關,儘可能將數據存儲和業務數據分離,減小業務數據庫的壓力。並且對數據的一致性要求不高。
- 每當訪問一個頁面就要存儲一條記錄,實時插入操做的要求很高,固然可使用緩存做爲臨時緩衝來解決數據頻繁更新的問題。
- 數據隨着訪問量的增加膨脹的很快,若是一個頁面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
第一個應用場景:當訪問某用戶的空間時,獲得某用戶最新的訪問記錄,同一個頁面重複訪問的話,返回最新的一次訪問。
java
- 首先是map方法,主要是定義outputCollection的結構。OutputCollection的輸出結構爲:{_id:key,value:value}
java 代碼
- String mapfun = "function(){emit({url:this.url,title:this.title},this.date)}";
java 代碼
- 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;}";
java 代碼
- DBObject query = newBasicDBObject();
- query.put("userid", userid);
- query.put("date", newBasicDBObject("$gte", fromDate));
- *.getCollection().mapReduce(mapfun, reducefun,"pageview_results", 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 代碼
- DBObject query = newBasicDBObject();
- query.put("entityId", entityId);
- query.put("entityName", entityName);
- query.put("date", newBasicDBObject("$gte", fromDate));
- query.put("date", newBasicDBObject("$lt", toDate));
- List sessionIds = this.mongoService.getCollection().distinct("sessionId", query);
- 定義map方法,主要是定義outputCollection的結構。OutputCollection的輸出結構爲:{_id:key,value:次數瀏覽的次數}
java 代碼
- String mapfun = " function(){emit({url:this.url,title:this.title},1)}";
java 代碼
- String reducefun = " function(key,vals){var count=0; for(var i in vals){count+=vals[i];} return count;}";
java 代碼
- *.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
服務器