【數(shù)據(jù)庫(kù)】MongoDB數(shù)據(jù)庫(kù)學(xué)習(xí)記錄

本次學(xué)習(xí)使用的是麥子學(xué)院的《mongodb最佳實(shí)踐課程》.侵刪.

安裝數(shù)據(jù)庫(kù)

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 #確保安裝的是最新的版本
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list #將mangodb添加到源
sudo apt-get update
sudo apt-get install -y mongodb-org

在ubuntu下mongodb默認(rèn)已經(jīng)啟動(dòng)

sudo service mongod stop #停止
sudo service mongod start #啟動(dòng)

打開(kāi)mongodb數(shù)據(jù)庫(kù)使用命令mongo麦轰,如果打開(kāi)弓候,可以看到版本
如果打開(kāi)失敗休玩,則再確認(rèn)下是否已經(jīng)啟動(dòng),使用上述命令重新啟動(dòng)一次即可

Paste_Image.png

創(chuàng)建并選擇一個(gè)庫(kù),我使用的庫(kù)名為test片择,圖個(gè)簡(jiǎn)單
use test #自動(dòng)創(chuàng)建并選擇一個(gè)庫(kù)

退出
exit

配置文件所在
/etc/mongod.conf

插入數(shù)據(jù)

插入數(shù)據(jù)時(shí)mongodb會(huì)自動(dòng)生成一個(gè)唯一的_id字段,其實(shí)這個(gè)_id也可以自行指定潜的。

db.students.indert({_id:"my_id",name:"whaike",age:24,contact:[{city:"深圳"},{phone:["1232354235"]}]})
插入測(cè)試數(shù)據(jù)
db.students.insert({name:"張三",school:{name:"清華大學(xué)",city:"北京"},age:19,gpa:3.97})
db.students.insert({name:"李四",school:{name:"北京大學(xué)",city:"北京"},age:20,gpa:3.3})
db.students.insert({name:"王二",school:{name:"交通大學(xué)",city:"上海"},age:22,gpa:3.68})
db.students.insert({name:"小牛",school:{name:"哈工大",city:"哈爾濱"},age:21,gpa:3.50})
db.students.insert({name:"小馬",school:{name:"交通大學(xué)",city:"西安"},age:21,gpa:3.70})
db.students.insert({name:"小朱"})

查詢(xún)

db.students.find() #查詢(xún)所有記錄,也可以使用db.students.find({})
db.students.find({name:"張三"}) #查詢(xún)name為張三的記錄
db.students.find({"school.name":"交通大學(xué)"}) #查詢(xún)學(xué)校名稱(chēng)為交通大學(xué)的記錄
db.students.find({"school.name":"交通大學(xué)","school.city":"西安"}) #查詢(xún)西安的交通大學(xué)
db.students.find({$or:[{"school.name":"交通大學(xué)"},{"school.city":"北京"}]}) #or運(yùn)算符,條件或字管,使用列表
db.students.find({$and:[{"school.name":"交通大學(xué)"},{"school.city":"上海"}]}) #and運(yùn)算符啰挪,條件與信不,使用列表
db.students.find({age:{$ne:20}})  #ne運(yùn)算符,條件不等于

也可以使用objectid進(jìn)行查詢(xún)

> db.students.find({"_id" : ObjectId("58ad4e19a169ed50558d1209")})
{ "_id" : ObjectId("58ad4e19a169ed50558d1209"), "name" : "王二", "school" : { "name" : "交通大學(xué)", "city" : "上海" }, "age" : 22, "gpa" : 3.68 }

使用正則進(jìn)行匹配

db.students.find({name:/^張/}) #匹配開(kāi)頭
db.students.find({name:/.*四/}) #匹配結(jié)尾

使用exists查詢(xún)某條記錄是否存在亡呵。

db.students.find({school:{$exists:false}}) #查詢(xún)記錄中沒(méi)有school的記錄

更新

通過(guò)find命令查看某條記錄的_id值抽活,這個(gè)值是mongodb自動(dòng)生成的<b>主鍵</b>,官網(wǎng)解釋為

A special 12-byte BSON type that guarantees uniqueness within the collection. The ObjectId is generated based on timestamp, machine ID, process ID, and a process-local incremental counter. MongoDB uses ObjectId values as the default values for _id fields.

12字節(jié)的BSON型數(shù)據(jù),前4字節(jié)表示時(shí)間戳锰什,然后3字節(jié)表示機(jī)器標(biāo)識(shí)碼下硕,然后2字節(jié)由進(jìn)程ID組成 (PID),最后3字節(jié)是隨機(jī)數(shù)。這樣做不僅可以保證其唯一性汁胆,還能保證在高并發(fā)的情況下依然保持唯一性梭姓。
該值其實(shí)是一個(gè)96位二進(jìn)制數(shù)。

使用_id值查找記錄嫩码,結(jié)合set關(guān)鍵字進(jìn)行精確更新操作如下

db.students.update({"_id" : ObjectId("58ad4e19a169ed50558d1207")},{$set:{age:18,gpa:3.95}}) #set關(guān)鍵字誉尖,精確操作

inc運(yùn)算符表示加法

db.students.update({"_id" : ObjectId("58ad4e19a169ed50558d1207")},{$inc:{age:1}}) #將age+1

max關(guān)鍵字可以保證記錄中的值為最大值,如果記錄中的值比它小則更新它谢谦,如果比它大則不更新释牺。min同理,保證最小值萝衩。

db.students.update({"_id" : ObjectId("58ad4e19a169ed50558d1207")},{$max:{age:21}})

mul為翻倍的操作

db.students.update({"_id" : ObjectId("58ad4e19a169ed50558d1207")},{$mul:{age:2}}) #age值做x2操作.

unset刪除記錄,精確刪除

db.students.update({"_id" : ObjectId("58ad4e19a169ed50558d1207")},{$unset:{school:true}})
更新2

假設(shè)想更新所有記錄回挽,將他們的age+1

> db.students.update({},{$inc:{age:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

由此可見(jiàn)mongodb默認(rèn)只更新一條記錄
如果要更新多條記錄

> db.students.update({},{$inc:{age:1}},{multi:true})
WriteResult({ "nMatched" : 6, "nUpserted" : 0, "nModified" : 6 })

multi表示找到一條記錄更新之后繼續(xù)尋找下一條進(jìn)行更新

setOnInsert中的內(nèi)容表示只在插入操作的時(shí)候有效们陆,upsert表示存在數(shù)據(jù)就更新沒(méi)有數(shù)據(jù)則插入洁奈。

> db.students.update({name:"Mike"},{$inc:{age:1},$setOnInsert:{school:{name:"new school"}}},{upsert:true}) 
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("58ae7693cf9b6da6d48b6c8f")
})

此時(shí)可以看到匹配到0條所以插入1條专酗。
當(dāng)我們?cè)俅螆?zhí)行時(shí)辟癌,其school的記錄已經(jīng)有了坷牛,故不會(huì)再發(fā)生插入操作亲桥,也就是setOnInsert字段后的內(nèi)容無(wú)效轩娶,即使你修改了其中的數(shù)據(jù)魄缚,它只會(huì)進(jìn)行age+1的操作了暗甥,如下

> db.students.update({name:"Mike"},{$inc:{age:1},$setOnInsert:{school:{name:"new school--2"}}},{upsert:true})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

BSON

BSON文檔:mongodb的根本喜滨,與json非常類(lèi)似,json-style document的形式撤防,bson是二進(jìn)制Bin的,而json是文本text的虽风。
mongodb有兩個(gè)特點(diǎn)

1、面向Document.他的增刪改查都是document的形式寄月,每一條記錄都是document.
2辜膝、Key-Value 的數(shù)據(jù)組織形式。

mongodb的優(yōu)勢(shì)在于他可以很容易將數(shù)據(jù)映射成key-value的形式漾肮,多數(shù)情況可以將程序中的object直接與數(shù)據(jù)庫(kù)達(dá)成此映射厂抖,很方便數(shù)據(jù)交換。
注意:

  1. _id字段為主鍵克懊,他的值覆蓋主鍵忱辅,如果你提供了此值則不會(huì)使用mongodb自動(dòng)提供的值七蜘。
  2. 所有Field不能以$開(kāi)頭,這個(gè)符號(hào)被mongodb保留使用了.
  3. name不能使用*.*這樣的格式,這個(gè)點(diǎn)好在key中很容易識(shí)別錯(cuò)誤.
  4. key可以重名墙懂,但最好不要崔梗,很容易查詢(xún)到錯(cuò)誤的value.
  5. value的類(lèi)型比較嚴(yán)格,因?yàn)榇嬖趎umberlong格式垒在,存入的數(shù)據(jù)如果是1則很可能存儲(chǔ)為"1"蒜魄,造成查詢(xún)時(shí)無(wú)法匹配,這個(gè)與使用的框架或語(yǔ)言有關(guān)场躯。
  6. mongodb對(duì)于單個(gè)的document的限制為16M.

內(nèi)嵌對(duì)象與reference

在MongoDB中谈为,表示關(guān)系有兩種辦法:
一種是嵌套(embedded),既是將一個(gè)文檔包裹一個(gè)子文檔踢关;
另一種是引用鏈接(reference link)伞鲫,使用MongoDB的DBRef對(duì)象建立文檔和文檔之間的關(guān)系。
關(guān)于表示關(guān)系的詳細(xì)說(shuō)明可以參看這篇博文
MongoDB數(shù)據(jù)庫(kù)關(guān)系表示和設(shè)計(jì):(1)嵌套文檔和引用鏈接

索引

查詢(xún)的三個(gè)方式

index seek #直接索引 - 快
index scan #掃索引 - 中
table scan #掃表 - 慢

mongodb使用BTREE實(shí)現(xiàn)索引签舞。
開(kāi)始使用name查詢(xún)記錄
創(chuàng)建索引

db.students.ensureIndex({name:1}) #為name創(chuàng)建索引

db.students.getIndexes()#查看是否存在索引
此時(shí)根據(jù)name查看數(shù)據(jù)就會(huì)走Btree索引的方式

> db.students.find({name:"Mike"}).explain()
{
    "cursor" : "BtreeCursor name_1",
        ..........

explain()可以查看執(zhí)行某條命令的具體信息秕脓。
創(chuàng)建聯(lián)合索引

db.students.ensureIndex({age:1,gpa:1})

此時(shí)數(shù)據(jù)庫(kù)中的順序會(huì)先按age排序儒搭,然后根據(jù)gpa排序,有點(diǎn)類(lèi)似DataFrame的Index吧.
此時(shí)查詢(xún)建立了索引的字段會(huì)走索引,例如

> db.students.find({age:{$gt:21},gpa:{$lt:3.6}}).explain()
{
    "cursor" : "BtreeCursor age_1_gpa_1",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 4,
        ........

單查某一個(gè)比如gpa則不會(huì)走此索引

> db.students.find({gpa:{$lt:3.6}}).explain()
{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 2,
    "nscannedObjects" : 7,
    "nscanned" : 7,
        ......

樹(shù)的表達(dá)和查詢(xún)

假設(shè)某平臺(tái)存在這樣的數(shù)據(jù)結(jié)構(gòu)

設(shè)計(jì)此document可以使用{_id:...,symble:"電腦"}
為保證某字段其唯一性魂仍,可以為其創(chuàng)建一個(gè)索引和一個(gè)唯一索引

 db.cats.ensureIndex({symbol:1},{unique:1})

那么如何表示其關(guān)系呢,
可以增加children字段
{_id:...,symble:"電腦",children:["平板","臺(tái)式機(jī)","筆記本"]}
如果覺(jué)得一個(gè)數(shù)組中存儲(chǔ)的數(shù)據(jù)太多,可以使用parent字段
{_id:...,symble:"電腦",children:["電子"]}
這樣查詢(xún)的時(shí)候使用遞歸或者其他方式進(jìn)行循環(huán)查詢(xún)可以得出特定需求的結(jié)果。

當(dāng)然還有更好的方式:

1、Ancestors Array遇伞。在每個(gè)子類(lèi)中使用一個(gè)字段專(zhuān)門(mén)用來(lái)記錄其所有的父輩爺輩等通路路徑上的所有節(jié)點(diǎn)巍耗,再為該字段創(chuàng)建索引,查詢(xún)的時(shí)候直接使用這個(gè)索引即可.

舉例:{_id:...,symble:"電腦",ancestors:["電子",電腦]}
建立索引:ensureIndex({ancestors:1})
查詢(xún):find({ancestors:"電子"})
這樣就可以查到所有acenstors中帶電子的記錄亲族,而且還是走的索引,效率比較高。

2、Path 。這是官方的做法榛斯。跟上一個(gè)有點(diǎn)類(lèi)似驮俗,但是它使用的是path字段,為string類(lèi)型聋丝,內(nèi)容為嚴(yán)格的路徑節(jié)點(diǎn)順序,并使用指定字符進(jìn)行分隔弱睦,例如"|",查詢(xún)的時(shí)候可以使用正則垒拢,find({$path:/^某節(jié)點(diǎn)名|節(jié)點(diǎn)名/})

舉例:{_id:...,symble:"電腦",ancestors:"電子|電腦"}
查詢(xún):find({$path:/^電子/}) #查詢(xún)電子下所有記錄
查詢(xún):find({$path:/^電子|電腦/}) #查詢(xún)電子-電腦下所有記錄

3、Model Tree Structures
詳見(jiàn)官方文檔Model Tree Structures

查詢(xún)拓展

find({條件},{映射(也叫投影技術(shù))})
find({},{字段名:0,name:1,age:1}) #0表示不需要求类,1表示需要

分頁(yè)技術(shù)
db.students.find({},{"_id":0,name:1,age:1}).limit(2).skip(2).sort({age:1}) #組合使用limit,skip,sort
limit表示每一頁(yè)多少條數(shù)據(jù),skip表示跳過(guò)多少條數(shù)據(jù),如果limit和skip一起使用尸疆,則跳過(guò)指定數(shù)據(jù)后會(huì)顯示之后limit條數(shù)據(jù).
排序中要使用字典方式,value的1表示順序-1表示倒序(排序的sort函數(shù)放在任何位置效果都是一樣的!這點(diǎn)與其他數(shù)據(jù)庫(kù)不一樣),倒序的排序也會(huì)優(yōu)先使用索引脖捻,而且是在順序的索引基礎(chǔ)上進(jìn)行reverse嗜浮。

數(shù)組內(nèi)嵌對(duì)象的查詢(xún)

簡(jiǎn)單查詢(xún)

插入數(shù)據(jù)

db.students.insert({name:"艾客",school:{name:"地球大學(xué)",city:"深圳"},courses:[{name:"MongoDB",grade:88},{name:"Java",grade:99}],age:19,gpa:3.9})

查詢(xún)

db.students.find({courses:{name:"MongoDB",grade:88}}) #精確查詢(xún)
db.students.find({"courses.name":"MongoDB"}) #模糊查詢(xún)所有匹配
db.students.find({"courses.0.name":"MongoDB"}) #數(shù)組中第n個(gè)元素(對(duì)象)的name為mongodb的
db.students.find({courses:{$size:2}}) #查詢(xún)ourses中恰好兩個(gè)元素的數(shù)據(jù)
復(fù)合查詢(xún)

插入數(shù)據(jù)

db.students.insert({name:"啊哈",school:{name:"宇宙大學(xué)",city:"深圳"},courses:[{name:"MongoDB",grade:88,quiz:[9,7,9,10]},{name:"Java",grade:98,quiz:[5,6,7,9]}],age:24,gpa:3.8})

查詢(xún)

db.students.find({courses:{$elemMatch:{name:"MongoDB",grade:{$gte:88}}}}) #復(fù)合查詢(xún)
db.students.find({courses:{$elemMatch:{name:"MongoDB",quiz:{$elemMatch:{$gt:3,$lt:5}}}}}) #復(fù)合查詢(xún)elemMatch
db.students.find({courses:{$elemMatch:{name:"MongoDB",quiz:{$all:[8,9]}}}}) #all匹配數(shù)組中的元素

更新插入

添加記錄

db.students.update({"_id" : ObjectId("58a903fbce4e9c33a3563dc3")},{$addToSet:{courses:{name:"Swift",grade:89}}}) 

addToSet可以保證數(shù)據(jù)的唯一性,如果存在記錄摩疑,則不會(huì)添加此字段,如果想要添加同樣的記錄兩條危融,則要使用push,從已有的元素中刪除使用pull(嚴(yán)格匹配,也就是值也要正確)雷袋,pull該條數(shù)據(jù)中所有符合條件的全部刪除吉殃,如果只刪除相同數(shù)據(jù)的其中一條則需要更精確的匹配<b>(這里需要再驗(yàn)證下)</b>。
2.4之前如果要添加或者刪除多條的時(shí)候使用pushAll,pullAll
版本2.4之后楷怒,push也可以插入多條

$push:{courses:{$each:[{name:"Swift",grade:89},{name:"C++",grade:87}],$sort:{grade:-1}}} 

上面的代碼在使用each之后蛋勺,此時(shí)的push可以使用數(shù)組進(jìn)行多個(gè)元素的添加,而且支持排序.

$push:{courses:{$each:[],$sort:{grade:1}}} #使用空的方括號(hào)鸠删,可以單獨(dú)進(jìn)行排序操作
$position:2 表示插入的位置抱完,從0開(kāi)始計(jì)算
$slice:2 切割,只顯示/保留數(shù)據(jù)數(shù)組中的前多少數(shù)據(jù)

以上三個(gè)sort/position/slice都是與each搭配使用的!

更精確的修改操作

db.students.update({"_id":ObjectId("......"),"courses.name":"Swift"},{$set:{"courses.$.grade":99}}) 

更精確的修改操作.courses.$.grade中使用$符號(hào)表示不知道查到的元素在該條數(shù)據(jù)中的位置,如果知道刃泡,可以使用數(shù)字巧娱,從0開(kāi)始。

db.students.update({"_id" : ObjectId("58a903fbce4e9c33a3563dc3"),"courses.name":"MongoDB"},{$push:{"courses.$.quiz":9.6}}) #為某條數(shù)據(jù)中某數(shù)組中的數(shù)組增加一個(gè)值

游標(biāo)cursor

var cursor = db.students.find({}) 
cursor.limit(2)#取出多少條記錄
cursor.count()#總共多少條記錄
cursor.objsLeftInBatch()查詢(xún)?nèi)〕鲆徊糠钟涗浿筮€剩下多少條數(shù)據(jù)
cursor.hasNext() #返回布爾值烘贴,是否還存在記錄禁添,也就是是否還可以執(zhí)行next()方法取得數(shù)據(jù)
cursor.next() #取出下一條數(shù)據(jù)
cursor.toArray() #將數(shù)據(jù)轉(zhuǎn)化為數(shù)組,可以通過(guò)[n]訪(fǎng)問(wèn)某一條記錄
db.students.find({}).map(function(doc){return doc.name}) #類(lèi)似js
db.students.find({}).map(function(doc){return {"name":doc.name,"gpa":doc.gpa/4*100}}) #可以寫(xiě)復(fù)雜點(diǎn)的函數(shù)
db.students.find().forEach(function(doc){print(doc.name)}) #forEach函數(shù)與map函數(shù)類(lèi)似也可以將參數(shù)逐個(gè)傳入進(jìn)行處理
db.students.find({},{"_id":0,age:1,gpa:1},hint({age:1,gpa:1})) #投影+排序  hint使用指定索引.強(qiáng)迫使用復(fù)合索引

地理位置查詢(xún)

先插入數(shù)據(jù)

db.pois.insert({name:"AAA Store",loc:{type:"Point",coordinates:[70,30]}})
db.pois.insert({name:"BBB Bank",loc:{type:"Point",coordinates:[69.99,30.01]}})
db.pois.insert({name:"CCC Park",loc:{type:"Polygon",coordinates:[[[70,30],[71,31],[71,30],[70,30]]]}})
db.pois.insert({name:"DDD Forest",loc:{type:"Polygon",coordinates:[[[65,68],[67,68],[67,69],[65,68]]]}})

為地理位置創(chuàng)建索引
db.pois.ensureIndex({loc:"2dsphere"})
結(jié)果自動(dòng)按照由近到遠(yuǎn)的順序
$maxDistance:1000000單位是米
如果不創(chuàng)建索引則查詢(xún)會(huì)報(bào)錯(cuò)code:17007

附近地點(diǎn)查詢(xún)
db.pois.find({loc:{
    $near:{
        $geometry:{
            type:"Point",
            coordinates:[70,30]
        },
        $maxDistance:1000000
    }
}})

區(qū)域的點(diǎn)組成的環(huán)需要封閉,也就是第一個(gè)點(diǎn)的坐標(biāo)跟最后一個(gè)點(diǎn)的坐標(biāo)需要相同,如下

區(qū)域內(nèi)地點(diǎn)查詢(xún)
db.pois.find({loc:{
    $geoWithin:{
        $geometry:{
            type:"Polygon",
            coordinates:[[[70,30],[71,31],[71,30],[70,30]]]
        }
    }
}})
使用geoNear command

runCommand對(duì)數(shù)據(jù)庫(kù)執(zhí)行一些命令

查詢(xún)
db.runCommand(
    {
        geoNear:"pois",
        near:{type:"Point",coordinates:[70,30]},
        spherical:true,
        minDistance:3000,
        maxDistance:7000
    }
)

得到結(jié)果

{
    "results" : [ ],
    "stats" : {
        "nscanned" : NumberLong(13),
        "objectsLoaded" : NumberLong(4),
        "avgDistance" : NaN,
        "maxDistance" : 0,
        "time" : 3
    },
    "ok" : 1
}

在查詢(xún)的結(jié)果集中

results以數(shù)組的形式保存查詢(xún)到的結(jié)果桨踪,數(shù)組中的每個(gè)元素都是一個(gè)objects老翘,擁有兩個(gè)key:
dis:距離,離當(dāng)前位置的距離
obj:查詢(xún)到的具體數(shù)據(jù)對(duì)象

狀態(tài)stats顯示統(tǒng)計(jì)數(shù)據(jù)
avgDistance:平均距離
maxDistance:最大距離

修改范圍使其可以查詢(xún)到數(shù)據(jù),驗(yàn)證results是否準(zhǔn)確酪捡,以下:

db.runCommand(
    {
        geoNear:"pois",
        near:{type:"Point",coordinates:[70,30]},
        spherical:true,
        minDistance:0,
        maxDistance:700000
    }
)

關(guān)于2dsphere索引叁征,詳見(jiàn)官網(wǎng)2dsphere Indexes

全文索引

首先插入數(shù)據(jù)

db.text.insert({content:"text performs a text search on the content of the fields indexed with a text index"})
db.text.insert({content:"When dealing with a small number of documents,.it is possible for the full-text-s"})
db.text.insert({content:"Soros enjoys playing mongo"})
db.text.insert({content:"Why don't you use mongo-db?"})

text創(chuàng)建全文索引
db.text.ensureIndex({content:"text"})
查詢(xún)
db.text.find({$text:{$search:"mongo"}})

也可以為search指定language

db.text.find({$text:{$search:"mongo",$language:"none"}}) #不區(qū)分語(yǔ)言
db.text.find({$text:{$search:"mongo",$language:"en"}}) #英文

數(shù)據(jù)庫(kù)管理初步

一般數(shù)據(jù)庫(kù)在運(yùn)行的時(shí)候只有一個(gè)服務(wù),那么如果它除了問(wèn)題停止運(yùn)行了怎么辦呢逛薇?
此時(shí)我們可以運(yùn)行兩個(gè)服務(wù)捺疼,當(dāng)?shù)谝粋€(gè)服務(wù)關(guān)閉的時(shí)候自動(dòng)切換到第二個(gè)服務(wù),這樣就可以保證數(shù)據(jù)庫(kù)的正常訪(fǎng)問(wèn)了永罚。

首先查看mongodb的進(jìn)程ps -ax | grep mongod
為了提高服務(wù)的高可用性,增加一個(gè)服務(wù)

sudo mongod --dbpath /srv/data --port 12333 #在前臺(tái)啟動(dòng)一個(gè)

此時(shí)我的ubuntu報(bào)錯(cuò)

 ERROR: dbpath (/srv/data) does not exist.
 Create this directory or give existing directory in --dbpath.
 See http://dochub.mongodb.org/core/startingandstoppingmongo

于是新建這個(gè)目錄sudo mkdir /srv/data,再次執(zhí)行時(shí)
顯示<code>waiting for connections on port 12333</code>時(shí)則表示成功了
可以使用ctrl+c結(jié)束啤呼,且不必?fù)?dān)心會(huì)有其他殘留問(wèn)題,這點(diǎn)可以從它自動(dòng)打印出來(lái)的信息得到.

前臺(tái)啟動(dòng)的方式總是不方便的呢袱,那么如何后臺(tái)啟動(dòng)呢

sudo mongod  --fork --logpath /var/log/mongodb2.log --dbpath /srv/data --port 12333 #在后臺(tái)啟動(dòng)一個(gè)服務(wù)

可以看到啟動(dòng)成功的信息

about to fork child process, waiting until server is ready for connections.
forked process: 2596
child process started successfully, parent exiting

連接其他進(jìn)程的mongo數(shù)據(jù)庫(kù)
mongo --port 12333
在數(shù)據(jù)庫(kù)內(nèi)
db.serverCmdLineOpts()可以查看連上的數(shù)據(jù)庫(kù)的詳細(xì)信息

一般使用后臺(tái)的方式如何停止呢

方式一
使用進(jìn)程ID進(jìn)行關(guān)閉
sudo kill (process id) #一般關(guān)閉數(shù)據(jù)庫(kù)

方式二
可以在數(shù)據(jù)庫(kù)內(nèi)關(guān)掉它
<small>admin用于管理當(dāng)前數(shù)據(jù)庫(kù)</small>

use admin
db.shutdownServer() #關(guān)閉當(dāng)前數(shù)據(jù)庫(kù)

方式三
使用dbpath在shell中指定關(guān)閉某個(gè)數(shù)據(jù)庫(kù)

sudo mongod --shutdown --dbpath /srv/data #使用dbpath指定關(guān)閉哪個(gè)數(shù)據(jù)庫(kù)

數(shù)據(jù)庫(kù)服務(wù)的監(jiān)控和profiling

db.runCommand({serverStatus:1}) #查看服務(wù)的詳細(xì)信息
db.runCommand({serverStatus:1}).pid #查看pid
db.runCommand({serverStatus:1}).mem #查看內(nèi)存
> db.runCommand({serverStatus:1}).mem
{
    "bits" : 64,
    "resident" : 64, #在RAM中所占用的空間
    "virtual" : 662, #虛擬內(nèi)存
    "supported" : true,
    "mapped" : 240,  #所有存儲(chǔ)空間
    "mappedWithJournal" : 480
}
mongodb使用內(nèi)存映射文件,映射到虛擬內(nèi)存官扣,然后像讀寫(xiě)內(nèi)存一樣來(lái)讀寫(xiě)文件,類(lèi)似操作系統(tǒng)的交換內(nèi)存方式.
> db.runCommand({serverStatus:1}).extra_info
{
    "note" : "fields vary by platform",
    "heap_usage_bytes" : 62586272,
    "page_faults" : 66 #這個(gè)值越高讀寫(xiě)效率越低
}
使用sharding減少數(shù)據(jù)量的方式或增加內(nèi)存的方式來(lái)降低page_faults值.

查看數(shù)據(jù)進(jìn)出量等

> db.runCommand({serverStatus:1}).network
{ "bytesIn" : 6160, "bytesOut" : 28570, "numRequests" : 69 }
如果numRequests過(guò)高可以考慮優(yōu)化一下document或者采用集群方案
> db.runCommand({serverStatus:1}).connections
{ "current" : 1, "available" : 51199, "totalCreated" : NumberLong(3) }
多少個(gè)客戶(hù)端正在鏈接數(shù)據(jù)庫(kù),總共允許多少個(gè),總共創(chuàng)建了多少個(gè)用戶(hù)

mongodb可以通過(guò)profile來(lái)監(jiān)控?cái)?shù)據(jù)羞福,進(jìn)行優(yōu)化惕蹄。

db.runCommand({dbStats:1})#數(shù)據(jù)庫(kù)的統(tǒng)計(jì)

打開(kāi)profile
> db.runCommand({profile:-1}) #記錄所有操作的時(shí)間
{ "was" : 0, "slowms" : 100, "ok" : 1 }

> db.runCommand({profile:1}) #只記錄比slowms慢的查詢(xún)
{ "was" : 0, "slowms" : 100, "ok" : 1 }

db.runCommand({profile:0}) #關(guān)閉profile

db.runCommand({profile:2}) #打開(kāi)慢查詢(xún)

db.system.profile.find({millis:{$gt:1}},{millis:1,op:1,query:1,ns:1}) #查看大于1ms的操作,后面{}中表示投影出來(lái)哪些數(shù)據(jù)

分布式屬性Replication

一組Mongodb復(fù)制集,就是一組mongod進(jìn)程治专,這些進(jìn)程維護(hù)同一個(gè)數(shù)據(jù)集合卖陵。復(fù)制集提供了數(shù)據(jù)冗余和高等級(jí)的可靠性。保證數(shù)據(jù)在生產(chǎn)部署時(shí)的冗余和可靠性张峰,通過(guò)在不同的機(jī)器上保存副本來(lái)保證數(shù)據(jù)的不會(huì)因?yàn)閱吸c(diǎn)損壞而丟失泪蔫。能夠隨時(shí)應(yīng)對(duì)數(shù)據(jù)丟失、機(jī)器損壞帶來(lái)的風(fēng)險(xiǎn)喘批。
Replica Set (cluster)
A replica set in MongoDB is a group of mongod processes that maintain the same data set.
其中撩荣,一個(gè)primary,多個(gè)secondary饶深,寫(xiě)只能在primary上
盜來(lái)一圖

Paste_Image.png

一組集群只有一個(gè)主導(dǎo)餐曹,他接收所有客戶(hù)端的操作,其他的Secondary獲得主服務(wù)器primary的數(shù)據(jù)并保持同步,主服務(wù)掛掉之后他們會(huì)通過(guò)選舉的方式定哪個(gè)secondary升級(jí)為primary以保證數(shù)據(jù)庫(kù)的正常運(yùn)行.

配置集群
whaike是為replication指定的一個(gè)名字,可以隨意起名
sudo mongod  --fork --logpath /var/log/mongodb2.log --dbpath /srv/data --port 12333 --replSet "whaike"
sudo mongod  --fork --logpath /var/log/mongodb3.log --dbpath /srv/data1 --port 12334 --replSet "whaike" 
sudo mongod  --fork --logpath /var/log/mongodb4.log --dbpath /srv/data2 --port 12335 --replSet "whaike" 

啟動(dòng)了三個(gè)
連接其中一個(gè)mongo --port 12333
鍵入rs.initiate()
查看狀態(tài)rs.status()

> rs.status()
{
    "set" : "whaike",
    "date" : ISODate("2017-02-24T08:36:51Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 0,
            "name" : "ubuntu:12333",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 263,
            "optime" : Timestamp(1487925371, 1),
            "optimeDate" : ISODate("2017-02-24T08:36:11Z"),
            "electionTime" : Timestamp(1487925371, 2),
            "electionDate" : ISODate("2017-02-24T08:36:11Z"),
            "self" : true
        }
    ],
    "ok" : 1
}
health值為1表示正常運(yùn)行
statuStr表示該數(shù)據(jù)庫(kù)的當(dāng)前角色

將另外兩個(gè)添加進(jìn)來(lái)
添加時(shí)使用的是name值,具體情況具體定

whaike:PRIMARY> rs.add("ubuntu:12334")
{ "ok" : 1 }
whaike:PRIMARY> rs.add("ubuntu:12335")
{ "ok" : 1 }

再次查看狀態(tài)

whaike:PRIMARY> rs.status()
{
    "set" : "whaike",
    "date" : ISODate("2017-02-24T08:43:32Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 0,
            "name" : "ubuntu:12333",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 664,
            "optime" : Timestamp(1487925805, 1),
            "optimeDate" : ISODate("2017-02-24T08:43:25Z"),
            "electionTime" : Timestamp(1487925371, 2),
            "electionDate" : ISODate("2017-02-24T08:36:11Z"),
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "ubuntu:12334",
            "health" : 1,
            "state" : 5,
            "stateStr" : "STARTUP2",
            "uptime" : 16,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-02-24T08:43:30Z"),
            "lastHeartbeatRecv" : ISODate("2017-02-24T08:43:31Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "initial sync need a member to be primary or secondary to do our initial sync"
        },
        {
            "_id" : 2,
            "name" : "ubuntu:12335",
            "health" : 1,
            "state" : 5,
            "stateStr" : "STARTUP2",
            "uptime" : 7,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-02-24T08:43:31Z"),
            "lastHeartbeatRecv" : ISODate("2017-02-24T08:43:30Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "initial sync need a member to be primary or secondary to do our initial sync"
        }
    ],
    "ok" : 1
}

可以看到添加成功.
我們插入一條數(shù)據(jù)試試

whaike:PRIMARY> db.students.insert({name:"testAAA"})
WriteResult({ "nInserted" : 1 })
whaike:PRIMARY> db.students.find()
{ "_id" : ObjectId("58aff25e7e8f7ae117685350"), "name" : "testAAA" }

插入并查詢(xún)成功!

現(xiàn)在我們將primary關(guān)閉以模擬該數(shù)據(jù)庫(kù)事故

use admin
db.shutdownServer()
exit #退出

重新進(jìn)入一個(gè)數(shù)據(jù)庫(kù)

test1@ubuntu:~$ mongo --port 12334
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12334/test
whaike:PRIMARY>

此時(shí)可以看到該復(fù)制集切換成了primary
鍵入rs.status()查看狀態(tài)
可以看出12333端口所關(guān)聯(lián)的數(shù)據(jù)庫(kù)的health已經(jīng)變?yōu)?粥喜,也就是不可用了,但是之前的STARTUP2變成了primary主導(dǎo).

此時(shí)如果還想擴(kuò)展其他端口關(guān)聯(lián)的副本進(jìn)來(lái)凸主,可以繼續(xù)使用rs.add()

其實(shí)創(chuàng)建Replication的時(shí)候可以一條命令添加多個(gè)復(fù)制集進(jìn)來(lái)

rs.initiate({"_id" : "whaike","members" : [
{"_id" : 0,"host" : "127.0.0.1:12333"},
{"_id" : 1,"host" : "127.0.0.1:12334"},
{"_id" : 2,"host" : "127.0.0.1:12335"},
]})

sharding分片技術(shù)

分片技術(shù),跟關(guān)系數(shù)據(jù)庫(kù)的表分區(qū)類(lèi)似额湘,我們知道當(dāng)數(shù)據(jù)量達(dá)到T級(jí)別的時(shí)候,我們的磁盤(pán)旁舰,內(nèi)存就吃不消了锋华,或者單個(gè)的mongoDB服務(wù)器已經(jīng)不能滿(mǎn)足大量的插入操作,針對(duì)這樣的場(chǎng)景,mongoDB提供的分片技術(shù)來(lái)應(yīng)對(duì)箭窜。
當(dāng)然分片除了解決空間不足的問(wèn)題之外毯焕,還極大的提升的查詢(xún)速度。
盜圖

Paste_Image.png

mongodb將數(shù)據(jù)分?jǐn)偟狡瑓^(qū)上,用戶(hù)代表整個(gè)數(shù)據(jù)庫(kù)纳猫,外面一切訪(fǎng)問(wèn)由通過(guò)用戶(hù)發(fā)起婆咸,而路由則負(fù)責(zé)對(duì)這些操作按一定的規(guī)則分?jǐn)偟讲煌钠瑓^(qū)執(zhí)行,這些規(guī)則保存在配置服務(wù)器中芜辕。

下面來(lái)個(gè)最簡(jiǎn)單的<b>例子</b>

使用mkdir按此路徑建立目錄

/srv/maizi/shardsconf/
/srv/maizi/data1/
/srv/maizi/data2/

啟動(dòng)一個(gè)配置服務(wù)器

sudo mongod --configsvr --port 12339 --dbpath /srv/maizi/shardsconf/ --fork --logpath /var/log/shardconf.log

啟動(dòng)一個(gè)router(路由)尚骄,用于轉(zhuǎn)發(fā)查詢(xún)服務(wù)等

sudo mongos --configdb 127.0.0.1:12339 --fork --port 12338 --logpath /var/log/mongos.log

連接mongos

mongo --port 12338
提示符會(huì)變?yōu)閙ongos>
exit #退出

啟動(dòng)正常
再啟動(dòng)幾個(gè)mongodb instance來(lái)做sharding服務(wù)

sudo mongod --fork --logpath /var/log/shard1.log --dbpath /srv/maizi/data1 --port 12336
sudo mongod --fork --logpath /var/log/shard2.log --dbpath /srv/maizi/data2 --port 12335

此時(shí)進(jìn)程中也許看不到router,但是連接正常侵续,暫且不管他

test1@ubuntu:~$ ps -ax|grep mongod
   991 ?        Ssl    0:20 /usr/bin/mongod --config /etc/mongod.conf
  2692 ?        Sl     0:01 mongod --configsvr --port 12339 --dbpath /srv/maizi/shardsconf/ --fork --logpath /var/log/shardconf.log
  2737 ?        Sl     0:00 mongod --fork --logpath /var/log/shard1.log --dbpath /srv/maizi/data1 --port 12336
  2751 ?        Sl     0:00 mongod --fork --logpath /var/log/shard2.log --dbpath /srv/maizi/data2 --port 12335
  2804 pts/0    S+     0:00 grep --color=auto mongod
test1@ubuntu:~$ mongo --port 12338
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12338/test
mongos> exit
bye

連接router

mongo --port 12338 #鏈接路由
sh.addShard("127.0.0.1:12336") #添加片區(qū)
sh.addShard("127.0.0.1:12335") #添加片區(qū)

use maizi
sh.enableSharding("maizi") #將麥子數(shù)據(jù)庫(kù)enableSharding
sh.shardCollection("maizi.students",{age:1}) #表層面也要enableSharding,還要指定根據(jù)什么規(guī)則來(lái)sharding,這里用年齡

往數(shù)據(jù)庫(kù)中插入10萬(wàn)條數(shù)據(jù)倔丈,可能會(huì)比較耗時(shí)

for(var i=0;i<100000;i++){db.students.insert({age:i%50,name:i});}

查看總條數(shù)

db.students.find().count()

查看如何分布

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 4,
    "minCompatibleVersion" : 4,
    "currentVersion" : 5,
    "clusterId" : ObjectId("58b387e88aa5cea1a8d87cb9")
}
  shards:
    {  "_id" : "shard0000",  "host" : "127.0.0.1:12336" }
    {  "_id" : "shard0001",  "host" : "127.0.0.1:12335" }
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "maizi",  "partitioned" : true,  "primary" : "shard0001" }
        maizi.students
            shard key: { "age" : 1 }
            chunks:
                shard0000   1
                shard0001   2
            { "age" : { "$minKey" : 1 } } -->> { "age" : 0 } on : shard0000 Timestamp(2, 0) 
            { "age" : 0 } -->> { "age" : 49 } on : shard0001 Timestamp(2, 2) 
            { "age" : 49 } -->> { "age" : { "$maxKey" : 1 } } on : shard0001 Timestamp(2, 3) 

mongos>

"primary" : "shard0001" 這條數(shù)據(jù)代表就算不sharding,數(shù)據(jù)也會(huì)存儲(chǔ)在這個(gè)sharding0001中
還可看到
maizi.students是這么分的
一共有shard0000和sharding0001兩張表
age從最小值到0分布在shard0000
從0到49分布在shard0001中
從49到最大值也分布在shard0001中
也就是年齡為正數(shù)的都是shard0001中

分別查看一下兩個(gè)sharding中的數(shù)據(jù)量

test1@ubuntu:~$ mongo --port 12335
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12335/test
> use maizi
switched to db maizi
> db.students.find().count()
100000
> exit
bye
test1@ubuntu:~$ mongo --port 12336
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12336/test
> use maizi
switched to db maizi
> db.students.find().count()
0
>

因?yàn)閿?shù)據(jù)本身并不好且根據(jù)年齡分布的規(guī)則也未設(shè)置好
所以目前所有的數(shù)據(jù)都分布在shard0001,而shard0000中沒(méi)有數(shù)據(jù)
但是sharding應(yīng)該是有效的
為了驗(yàn)證sharding的有效性
切換到mongos來(lái)插入一條年齡為負(fù)數(shù)的數(shù)據(jù)

test1@ubuntu:~$ mongo --port 12338
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12338/test
mongos> use maizi
switched to db maizi
mongos> db.students.insert({name:"xiaotingzi",age:-26})
WriteResult({ "nInserted" : 1 })

然后切換到shard0000也就是12336端口進(jìn)行查看<small>(關(guān)于這點(diǎn)可以在狀態(tài)分布的結(jié)果中状蜗,從shards字段看出)</small>

test1@ubuntu:~$ mongo --port 12336
MongoDB shell version: 2.6.12
connecting to: 127.0.0.1:12336/test
> use maizi
switched to db maizi
> db.students.find()
{ "_id" : ObjectId("58b395c5a04059cd2fbb6ae0"), "name" : "xiaotingzi", "age" : -26 }
>

所以需五,sharding有效,通過(guò)規(guī)則區(qū)分?jǐn)?shù)據(jù)已經(jīng)成功

關(guān)于shard也可以查看文檔知識(shí)庫(kù)MongoDB 3.0 常見(jiàn)集群的搭建(主從復(fù)制,副本集轧坎,分片....)

數(shù)據(jù)備份與恢復(fù)

1宏邮、文件系統(tǒng)快照
2、mongodb提供的工具
    1.mongodump #更靈活的操作缸血,缺點(diǎn):慢
    2.mongorestore

詳情見(jiàn)官網(wǎng)MongoDB Backup Methods

安全security checklist

1蜜氨、Authentication
2、Authorization

mongodb默認(rèn)的內(nèi)部通信是明文的属百,所以需要安置在可信任的環(huán)境中.
如果要使用用戶(hù)名和密碼進(jìn)行訪(fǎng)問(wèn)則需要自己在配置文件中配置
詳見(jiàn)官網(wǎng)Security Checklist

服務(wù)器端腳本

mongodb2.4版本之后服務(wù)端使用的是js的V8引擎來(lái)解析服務(wù)器端的JS腳本
用回之前的數(shù)據(jù)庫(kù)mongo test

通過(guò)where可以使用js函數(shù)
db.students.find({$where:function(){return obj.age>22;}})
db.students.find({$where:function(){return typeof obj.courses != "undefined" && obj.courses.length >=1;}})
db.students.find({$where:"obj.age>22"}) #也可以直接使用字符串

注意:$where后面的內(nèi)容返回的一定是一個(gè)布爾型
詳情見(jiàn)官網(wǎng)Server-side JavaScript

可以自定義JavaScript函數(shù)保存在數(shù)據(jù)庫(kù)中,使用時(shí)用db.eval(...)調(diào)用

db.system.js.save(
    {_id:"echoFunction",
    value:function(x){return x;}
    }
)
db.eval("echoFunction('test')")

詳見(jiàn)官網(wǎng)Store a JavaScript Function on the Server

事務(wù)

<b>ACID</b>
A:原子型 :
C:一致性 :更新一般服務(wù)器問(wèn)題记劝,不會(huì)出現(xiàn)臟數(shù)據(jù)
C:隔離性 :更新過(guò)程其他進(jìn)程無(wú)法讀取
D:分卷技術(shù) :將要進(jìn)行的操作寫(xiě)入日志里,日志記錄完畢之后會(huì)提示操作成功族扰,此時(shí)其實(shí)并沒(méi)有成功厌丑,如果這時(shí)數(shù)據(jù)庫(kù)異常,則日志里還有記錄渔呵,可以保證數(shù)據(jù)完整怒竿。之后再有該日志來(lái)修改數(shù)據(jù)庫(kù)數(shù)據(jù).

所往一個(gè)document更新數(shù)據(jù) ,符合一致性扩氢,要么更新的幾個(gè)參數(shù)一起OK耕驰,要么都更新不上,所以單個(gè)文檔支持事務(wù)录豺。

但是多個(gè)文檔則不支持
但是可以通過(guò)某種算法或者技術(shù)來(lái)實(shí)現(xiàn)它
詳見(jiàn)官網(wǎng)Perform Two Phase Commits
雖然可以實(shí)現(xiàn)朦肘,但是依舊要避免出現(xiàn)這種情況。

數(shù)據(jù)聚合

在這里使用一個(gè)可視化軟件robomongo-1.0.0-rc1-linux-x86_64-496f5c2.tar.gz
解壓之后進(jìn)入bin目錄直接執(zhí)行robomongo即可打開(kāi),配置只有一個(gè)連接本地或者遠(yuǎn)程双饥,端口默認(rèn)媒抠。

這個(gè)工具還不錯(cuò)
鍵入命令

db.students.count({age:{$gt:22}}) #統(tǒng)計(jì)年齡大于22的一共有多少個(gè),最簡(jiǎn)單的聚合
Paste_Image.png

鍵入

db.students.distinct("age") #統(tǒng)計(jì)數(shù)據(jù)庫(kù)中所有的年齡咏花,會(huì)自動(dòng)去重

顯示方式可以分好幾種趴生,我使用的是紅色標(biāo)注的字符模式


Paste_Image.png

統(tǒng)計(jì)每個(gè)年齡層下有多少人

db.students.group({
        key:{age:1},
        reduce:function(cur,result){result.count+=1},
        initial:{count:0}
    })
db.students.group({
        key:{age:1}, //分組依據(jù)
        cond:{age:{$exists:true},gpa:{$exists:true}}, //這里表示只操作有數(shù)據(jù)的部分
        reduce:function(cur,result){result.count+=1;result.total_gpa += cur.gpa;result.ava_gpa = result.total_gpa / result.count;},  //顯示字段與運(yùn)算規(guī)則
        initial:{count:0,total_gpa:0} //初始值
    })
Paste_Image.png
數(shù)據(jù)聚合流水線(xiàn)
db.students.aggregate(
    [
        {$match:{age:{$exists:true}}},
        {$group:{_id:"$age",count:{$sum:1},total_gpa:{$sum:"$gpa"}}}
    ]
)

$age表示從上一個(gè)的輸出結(jié)果中拿到age,其他字段類(lèi)似

流水線(xiàn)的分組聚合方式中,各個(gè)工位在數(shù)組中以逗號(hào)分隔苍匆,工位是可以增加的

db.students.aggregate(
    [
        {$match:{age:{$exists:true}}},
        {$group:{_id:"$age",count:{$sum:1},total_gpa:{$sum:"$gpa"}}},
        {$project:{_id:1,count:1,ava_gpa:{$divide:["$total_gpa","$count"]}}}
    ]
)
db.students.aggregate(
    [
        {$match:{age:{$exists:true}}}, //拿到符合要求的數(shù)據(jù)
        {$group:{_id:"$age",count:{$sum:1},total_gpa:{$sum:"$gpa"}}}, //根據(jù)年齡分組刘急,統(tǒng)計(jì)
        {$project:{_id:1,count:1,ava_gpa:{$divide:["$total_gpa","$count"]}}}, //投影與計(jì)算平均值
        {$sort:{ava_gpa:-1}} #排序
    ]
)

上一步的計(jì)算平均值可以使用內(nèi)置的函數(shù)avg,效果一樣

db.students.aggregate(
    [
        {$match:{age:{$exists:true}}},
        {$group:{_id:"$age",count:{$sum:1},ava_gpa:{$avg:"$gpa"}}},
        {$sort:{ava_gpa:-1}}
    ]
)

不同年齡的選課統(tǒng)計(jì)

db.students.aggregate(
    [
        {$match:{age:{$exists:true},courses:{$exists:true}}},
        {$project:{age:1,courses_count:{$size:"$courses"}}}, //在這里先對(duì)數(shù)據(jù)進(jìn)行投影可以減少數(shù)據(jù)量浸踩,減小內(nèi)存使用叔汁,避免浪費(fèi)資源
        {$group:{_id:"$age",acc:{$avg:"$courses_count"}}},
        {$sort:{acc:-1}}
    ]
)

不能年齡層選課種類(lèi)統(tǒng)計(jì)

db.students.aggregate(
    [
        {$match:{age:{$exists:true},courses:{$exists:true}}},
        {$project:{age:1,"courses.name":1}}, //直接投影課程的名字
        {$unwind:"$courses"}, //展開(kāi)數(shù)組
        {$group:{_id:"$age",c_names:{$addToSet:"$courses.name"}}}, //將同齡的人選的所有課程進(jìn)行合并
        {$project:{_id:1,c_names:1,cc:{$size:"$c_names"}}}, //統(tǒng)計(jì)
        {$sort:{cc:-1}} //最后根據(jù)cc排序
    ]
)

詳細(xì)信息可以查看官方文檔Aggregation Pipeline Operators

MapReduce

流水線(xiàn)方式的操作受文檔大小限制,且在內(nèi)存中執(zhí)行民轴,不適合大數(shù)據(jù)處理攻柠,但是mapReduce是將數(shù)據(jù)存儲(chǔ)在一個(gè)collection中進(jìn)行處理,可以操作很大量的數(shù)據(jù)后裸,適合大數(shù)據(jù)處理

db.students.mapReduce(
    function(){ //map函數(shù)瑰钮,起到過(guò)濾器的作用,emit發(fā)射數(shù)據(jù)
        emit(this.age,this.gpa);
    },
    function(key,values){ //reduce函數(shù) 這里values如果有多個(gè)值則以數(shù)組形式保存
        return Array.sum(values)/values.length;
    },
    { //options
        query:{age:{$exists:true},gpa:{$exists:true}}, //過(guò)濾條件
        out:{inline:1} //輸出
    }
)
db.students.mapReduce(
    function(){ //map函數(shù),起到過(guò)濾器的作用,emit發(fā)射數(shù)據(jù)
        emit(this.age,this.gpa); //發(fā)射出來(lái)的兩個(gè)參數(shù)中微驶,age作為key浪谴,gpa作為value,如果key下的value不止一個(gè)值,那么才會(huì)進(jìn)入reduce進(jìn)行進(jìn)一步處理,且傳入的value是一個(gè)數(shù)組因苹,如果只有一個(gè)則不會(huì).發(fā)射出的數(shù)據(jù)受到單個(gè)bson的object大小限制(16M)且最多只能發(fā)射一半(8M)
    },
    function(key,values){ //reduce函數(shù) 這里values如果有多個(gè)值則以數(shù)組形式保存
        return Array.sum(values)/values.length;
    },
    { //options
        query:{age:{$exists:true},gpa:{$exists:true}}, //過(guò)濾條件
        out:{inline:1} //輸出,out:"avg_gpa" //輸出到一個(gè)collections中,可以持久化保存苟耻,可以用于真正的大數(shù)據(jù)處理,inline操作在內(nèi)存中運(yùn)行受內(nèi)存大小限制,也受到單個(gè)bson的object大小限制(16M)且最多只能發(fā)射一半(8M)
    }
)

db.students.mapReduce(
    function(){
        if(this.age>20){
            emit("old",this.gpa);
        }else{
            emit("yong",this.gpa)
        }
    },
    function(key,values){
        return {gpa_array:values};
    },
    {
        query:{age:{$exists:true},gpa:{$exists:true}},
        out:{inline:1}
    }
)
db.students.mapReduce(
    function(){
        var course_names = this.courses.map(
            function(course){
                return course.name
            });
        var intersection_names = course_names.filter(
            function(c_name){
                return params.indexOf(c_name) != -1
            });
        var diff_names = course_names.filter(
            function(c_name){
                return params.indexOf(c_name) == -1
            });
        diff_names.forEach(
            function(c_name){
                emit(c_name,intersection_names.length / course_names.length)
            });
    },
    function(key,values){
        var total = values.reduce(function(p,c){
            return p + c;
        });
        return total;
    },
    {
        query:{courses:{$exists:true}},
        out:{inline:1},
        scope:{params:["MongoDB"]}
    }
)

官方文檔mapReduce

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扶檐,一起剝皮案震驚了整個(gè)濱河市凶杖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌款筑,老刑警劉巖智蝠,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奈梳,居然都是意外死亡杈湾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)攘须,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漆撞,“玉大人,你說(shuō)我怎么就攤上這事于宙「〔担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵捞魁,是天一觀的道長(zhǎng)抹恳。 經(jīng)常有香客問(wèn)我,道長(zhǎng)署驻,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮旺上,結(jié)果婚禮上瓶蚂,老公的妹妹穿的比我還像新娘。我一直安慰自己宣吱,他們只是感情好窃这,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著征候,像睡著了一般杭攻。 火紅的嫁衣襯著肌膚如雪疤坝。 梳的紋絲不亂的頭發(fā)上锅睛,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音历谍,去河邊找鬼现拒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛望侈,可吹牛的內(nèi)容都是我干的印蔬。 我是一名探鬼主播侥猬,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼岂丘!你這毒婦竟也來(lái)了铜邮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寨蹋,失蹤者是張志新(化名)和其女友劉穎松蒜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體已旧,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秸苗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了运褪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惊楼。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玖瘸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出檀咙,到底是詐尸還是另有隱情雅倒,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布弧可,位于F島的核電站蔑匣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏棕诵。R本人自食惡果不足惜裁良,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望校套。 院中可真熱鬧价脾,春花似錦、人聲如沸搔确。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膳算。三九已至座硕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涕蜂,已是汗流浹背华匾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留机隙,地道東北人蜘拉。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像有鹿,于是被迫代替她去往敵國(guó)和親旭旭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容