1、文檔結(jié)構(gòu)示例
{
_id: xxxx,
user: 'xiaoming',
level: 5,
from: 'iPhone',
info: 'something wrong'
}
2、場景:user為'xiaoming'的文檔有六七百萬條
3、問題:怎么提升aggregate+group+sum速度
aggregate([
{$match:{user: 'xiaoming', info:{$regex:'wrong'}}},
{$group:{_id:null, count:{$sum:1}}}
])
用上面這個來統(tǒng)計xiaoming帶有wrong的文檔數(shù)量,結(jié)果
{"_id": null, "count": 2299999 }
耗時30s-40s。user、info、user+info三種索引都嘗試過,速度都沒有提升
baidu、google查到‘帶條件計數(shù)慢無解’
怎么提升效率,10s以內(nèi)能實現(xiàn)嗎
學習是最好的投資!
首先要說明的一個問題是,對于OLAP型的操作,期望不應該太高。畢竟是對于大量數(shù)據(jù)的操作,光從IO就已經(jīng)遠超通常的OLTP操作,所以要求達到OLTP操作的速度和并發(fā)是不現(xiàn)實的,也是沒有意義的。但并不是說一點優(yōu)化空間也沒有。
我們先從索引入手。在沒有索引的前提下,找出600萬條{user: "xiaoming"}
需要多少時間?全表掃描COLLSCAN
從700w條數(shù)據(jù)中找出600w條,跟從1億條數(shù)據(jù)中找出600w條顯然是兩個概念。命中索引IXSCAN
,這個差異就會小很多,幾乎可以忽略。所以你說{user: 1}
這個索引沒有作用是不對的,可能只是因為集合數(shù)據(jù)量太少看不出差異而已。順便應該提一下看效率是否有差異應該看執(zhí)行計劃,不要看執(zhí)行時間,時間是不準確的。
在有user
索引的前提下,結(jié)果仍然有600w條,剩下的部分是個regex
,regex
無法命中索引,所以不管有沒有對info
的索引都沒有意義。在找到600w條數(shù)據(jù)之后還有一個對600w數(shù)據(jù)的filter
操作。唯一對這個操作可能有幫助的只有全文索引
,但全文索引并不能完全替代正則,具體問題需要讀一下文檔??紤]全文索引可行的情況下,可以建立復合索引:
db.coll.createIndex({
user: 1,
info: "text"
});
對應地查詢應該改為:
db.coll.aggregate([
{$match:{user: 'xiaoming', $text: { $search: "wrong" }}},
{$group:{_id:null, count:{$sum:1}}}
])
關(guān)于復合全文索引的介紹參考這里,仍然是有些限制需要注意。這樣優(yōu)化之后預計在同樣的硬件下能降到20s以內(nèi),跟你要的10s內(nèi)還有一段距離。原因開頭說了,對OLAP就不能期望這么高。如果你真有這方面的需求,就應該從源頭入手,考慮:
每次info
字段有更新或插入時就做好計數(shù)
或者
每隔一段時間做一次完整的統(tǒng)計,緩存統(tǒng)計結(jié)果,查詢的時候直接展現(xiàn)給用戶
不了解,不過能不能拆分成兩個match會不會好一點呢。。
類似于
aggregate([
{$match:{user: 'xiaoming'}},
{$match:{info: /wrong/}},
{$group:{_id:null, count:{$sum:1}}}
])
主要我認為正則花時間。
有index的話,index一下user。