公司是作互聯網廣告投放的,須要統計廣告展示量在前五百的域名。最簡單粗暴的作法就是group by,根據url分組,而後再sort一下就搞定曬!結果問題就出現了。java
以下統計的2015-02-28當日22時的日誌,文檔數量:904405。web
db['log.2015-02-28_22'].group({ key : {domainUrl:1}, initial : {count:0}, reduce : function Reduce(doc, out) { out.count++; }
報錯:mongodb
Error in executing GroupBy Command 'group' failed: exception: group() can't handle more than 20000 unique keys (response: { "errmsg" : "exception: group() can't handle more than 20000 unique keys", "code" : 17203, "ok" : 0.0 }) Type: MongoDB.Driver.MongoCommandException
錯誤信息不夠具體,加之理解問題,我覺得是集合中的文檔數量不能超過20000,2萬都不能支持那要group幹嗎。通過屢次測試,終於證實了我是錯的。是不支持大於2萬的結果集,即分組後產生的文檔數量不能超過2萬。app
//集合大小 > db['log.2015-02-28_22'].count(); 904405 //惟一url數量 > db['log.2015-02-28_22'].distinct('domainUrl').length; 20738
這個集合中的惟一url數量是20738,恰好超過了2萬,因此MongoDB的group就無能爲力了。dom
另外測試還意外發現,distinct對結果集大小也是有限制的。結果集大小不能超過16Mb。函數
> db['log.2015-02-28_22'].distinct('webUrl').length; distinct failed: { "errmsg" : "exception: distinct too big, 16mb cap", "code" : 17217, "ok" : 0
MongoDB爲何這麼多限制,並且閾值都比較小,還請大神指點。學習
問題是用來解決的,經過stackoverflow大神的指點,我嘗試着用MongoDB的mapreduce搞定它,其實一開始我是拒絕的,由於,你不能讓我用,我就用;首先,我要看一下我會不會用。結果發現,我不會用,而後看了看MongoDB的說明文檔。寫出以下代碼:測試
db.runCommand({ mapreduce: "log.2015-02-28_22", map : function Map() { emit( { uuid:this.adUUId, //對不一樣的廣告統計url url:this.domainUrl }, {count: 1} ); }, reduce : function Reduce(key, values) { var total=0; for( var i in values){ total +=values[i].count; } return {count:total}; }, finalize : function Finalize(key, reduced) { return reduced; }, out : { inline : 1 } });
我用了一次,感受效果還不錯。如今也推薦給你試一下。ui
結果對象的結構以下:this
{ "_id" : { "uuid" : "inmobi" , "url" : "static.51y5.net" } , "value" : { "count" : 82409.0}} { "_id" : { "uuid" : "inmobi" , "url" : "applet.kakamobi.com"} , "value" : { "count" : .23714.0}}
_id屬性中是分組字段,value屬性中保存結果對象。mongodb聚合函數產生的count是double類型的。你能夠在finalize方法中處理一下,轉換成整型。我是在java 代碼轉換的。
MongoDB的Mapreduce我也是第一次用,更具體的用法詳解,待我研究以後,再作報告。由於我不肯意寫完之後,再加點特技上去,文章「duang」一下,很贊,很勁,這樣讀者出來必定會罵我,博主根本就是不會,還裝逼。(just kidding.)
以上內容有不對的地方,還望你們指正,虛心學習。
Thanks a lot ,END!