什么是MongoDB?
mongodb是一個基于分布式文件儲存的數(shù)據(jù)庫,由C++編寫。是一個文檔型數(shù)據(jù)庫,提供好的性能,領(lǐng)先的非關(guān)系型數(shù)據(jù)庫
MongoDB的儲存形式類似于Python的字典,以{‘key’:‘value’}的形式儲存
mongoDB適用于哪些場景?
1.網(wǎng)站數(shù)據(jù)量大
2,網(wǎng)站數(shù)據(jù)讀寫操作頻繁
3.價值較低
數(shù)據(jù)庫MySQL和mongdb的區(qū)別?
SQL:mysql、Oracle、sqlserver、db2
- 高度事務(wù)性場景:銀行、會計、貿(mào)易,庫管,需要大量原子性操作
- 適合存儲結(jié)構(gòu)化數(shù)據(jù),如用戶的帳號、地址,預(yù)先定義明確的字段
- 數(shù)據(jù)價值高、對安全性要求高、穩(wěn)定性要求高
- 需要持久化存儲的 "冷數(shù)據(jù)"(不需要經(jīng)常讀寫的數(shù)據(jù))
- 需要通過SQL語言做關(guān)聯(lián)查詢,比如join
6.這些數(shù)據(jù)的規(guī)模、增長的速度通常是可以預(yù)期的
NoSQL:
redis key:value(string、hash、set、zset、list)、
mongodb {"name" : "xiaoming", "age" : 18}, {}
mongodb:字典格式,支持分組、索引、主從備份、集群
- 靈活的數(shù)據(jù)結(jié)構(gòu),適合存儲非結(jié)構(gòu)化數(shù)據(jù),如文章、評論,需要事先設(shè)計 數(shù)據(jù)的增刪改 的字段
- 高度收縮性場景,社交網(wǎng)絡(luò)、熱點資訊,NoSQL數(shù)據(jù)庫通常具有無限(至少接近)伸縮性
- 處理 "熱數(shù)據(jù)"(經(jīng)常需要讀寫的數(shù)據(jù)),這些數(shù)據(jù)通常用于模糊處理,如全文搜索、機器學習
- 這些數(shù)據(jù)是海量的,而且增長的速度是難以預(yù)期的,更容易擴展
- 按key獲取數(shù)據(jù)效率很高,但是對join或其他結(jié)構(gòu)化查詢的支持就比較差
大型互聯(lián)網(wǎng)項目都會選用MySQL(或任何關(guān)系型數(shù)據(jù)庫) + NoSQL的組合方案
端口號:
mysql: 3306
redis: 6379
mongodb: 27017
mongodb安裝教程
關(guān)于mongodb的好處,優(yōu)點之類的這里就不說了,唯一要講的一點就是mongodb中有三元素:數(shù)據(jù)庫,集合,文檔,其中“集合”
就是對應(yīng)關(guān)系數(shù)據(jù)庫中的“表”,“文檔”對應(yīng)“行”。
下載
上MongoDB官網(wǎng) ,我們發(fā)現(xiàn)有32bit和64bit,這個就要看你系統(tǒng)了,不過這里有兩點注意:
①:根據(jù)業(yè)界規(guī)則,偶數(shù)為“穩(wěn)定版”(如:1.6.X,1.8.X),奇數(shù)為“開發(fā)版”(如:1.7.X,1.9.X),這兩個版本的區(qū)別相信大家都知道吧。
②:32bit的mongodb最大只能存放2G的數(shù)據(jù),64bit就沒有限制。
我這里就下載"2.0.2版本,32bit“,ok,下載之后我就放到”E盤“,改下文件夾名字為”mongodb“。
啟動
①:啟動之前,我們要給mongodb指定一個文件夾,這里取名為”db",用來存放mongodb的數(shù)據(jù)。
②:微軟徽標+R,輸入cmd,首先找到“mongodb”的路徑,然后運行mongod開啟命令,同時用--dbpath指定數(shù)據(jù)存放地點為“db”文件夾。
③:最后要看下是否開啟成功,從圖中的信息中獲知,mongodb采用27017端口,那么我們就在瀏覽器里面鍵入“http://localhost:27017/”,
打開后,mongodb告訴我們在27017上Add 1000可以用http模式查看mongodb的管理信息。
基本操作
由于是開篇,就大概地說下基本的“增刪查改“,我們再開一個cmd,輸入mongo命令打開shell,其實這個shell就是mongodb的客戶端,
同時也是一個js的編譯器,默認連接的是“test”數(shù)據(jù)庫。
insert 操作
好,數(shù)據(jù)庫有了,下一步就是集合,這里就取集合名為“person”,要注意的就是文檔是一個json的擴展(Bson)形式。
find 操作
我們將數(shù)據(jù)插入后,肯定是要find出來,不然插了也白插,這里要注意兩點:
① “_id": 這個字段是數(shù)據(jù)庫默認給我們加的GUID,目的就是保證數(shù)據(jù)的唯一性。
② 嚴格的按照Bson的形式書寫文檔,不過也沒關(guān)系,錯誤提示還是很強大的。
update操作
update方法的第一個參數(shù)為“查找的條件”,第二個參數(shù)為“更新的值”,學過C#,相信還是很好理解的。
remove操作
remove中如果不帶參數(shù)將刪除所有數(shù)據(jù),呵呵,很危險的操作,在mongodb中是一個不可撤回的操作,三思而后行。
細說增刪改查
有一天當我們用上一篇同樣的方式打開mongodb,突然
傻眼了,擦,竟然開啟不了,仔細觀察“劃線區(qū)域“的信息,發(fā)現(xiàn)db文件夾下有一個類似的”lock file”阻止了mongodb的開啟,接下來我們要做的就
是干掉它,之后,開啟成功,關(guān)于mongodb的管理方式將在后續(xù)文章分享。
一: Insert操作
上一篇也說過,文檔是采用“K-V”格式存儲的,如果大家對JSON比較熟悉的話,我相信學mongodb是手到擒來,我們知道JSON里面Value
可能是“字符串”,可能是“數(shù)組”,又有可能是內(nèi)嵌的一個JSON對象,相同的方式也適合于BSON。
常見的插入操作也就兩種形式存在:“單條插入”和“批量插入”。
① 單條插入
先前也說了,mongo命令打開的是一個JAVAscript shell。所以js的語法在這里面都行得通,看起來是不是很牛X。
② 批量插入
這玩意跟“單條插入”的差異相信大家應(yīng)該知道,由于mongodb中沒有提供給shell的“批量插入方法”,沒關(guān)系,各個語言的driver都打通
了解mongodb內(nèi)部的批量插入方法,因為該方法是不可或缺的,如果大家非要模擬下批量插入的話,可以自己寫了for循環(huán),里面就是insert。
二:Find操作
日常開發(fā)中,我們玩查詢,玩得最多的也就是二類:
①: >, >=, <, <=, !=, =。
②:And,OR,In,NotIn
這些操作在mongodb里面都封裝好了,下面就一一介紹:
<1>"gt", "gt","gte", "lt", "lt","lte", "$ne", "沒有特殊關(guān)鍵字",這些跟上面是一一對應(yīng)的,舉幾個例子。
<2> "無關(guān)鍵字“, "or", "or","in","$nin" 同樣我也是舉幾個例子
<3> 在mongodb中還有一個特殊的匹配,那就是“正則表達式”,這玩意威力很強的。
<4> 有時查詢很復(fù)雜,很蛋疼,不過沒關(guān)系,mongodb給我們祭出了大招,它就是where,為什么這么說,是因為where,為什么這么說,是因為where中的value
就是我們非常熟悉,非常熱愛的js來助我們一馬平川。
三:Update操作
更新操作無非也就兩種,整體更新和局部更新,使用場合相信大家也清楚。
<1> 整體更新
不知道大家可還記得,我在上一篇使用update的時候,其實那種update是屬于整體更新。
<2> 局部更新
有時候我們僅僅需要更新一個字段,而不是整體更新,那么我們該如何做呢?easy的問題,mongodb中已經(jīng)給我們提供了兩個
修改器: inc 和inc和set。
① $inc修改器
$inc也就是increase的縮寫,學過sql server 的同學應(yīng)該很熟悉,比如我們做一個在線用戶狀態(tài)記錄,每次修改會在原有的基礎(chǔ)上
自增$inc指定的值,如果“文檔”中沒有此key,則會創(chuàng)建key,下面的例子一看就懂。
② $set修改器
啥也不說了,直接上代碼
<3> upsert操作
這個可是mongodb創(chuàng)造出來的“詞”,大家還記得update方法的第一次參數(shù)是“查詢條件”嗎?,那么這個upsert操作就是說:如果我
沒有查到,我就在數(shù)據(jù)庫里面新增一條,其實這樣也有好處,就是避免了我在數(shù)據(jù)庫里面判斷是update還是add操作,使用起來很簡單
將update的第三個參數(shù)設(shè)為true即可。
<4> 批量更新
在mongodb中如果匹配多條,默認的情況下只更新第一條,那么如果我們有需求必須批量更新,那么在mongodb中實現(xiàn)也是很簡單
的,在update的第四個參數(shù)中設(shè)為true即可。例子就不舉了。
四: Remove操作
這個操作在上一篇簡單的說過,這里就不贅述了。
細說高級操作
今天跟大家分享一下mongodb中比較好玩的知識,主要包括:聚合,游標。
一: 聚合
常見的聚合操作跟sql server一樣,有:count,distinct,group,mapReduce。
count
count是最簡單,最容易,也是最常用的聚合工具,它的使用跟我們C#里面的count使用簡直一模一樣。
distinct
這個操作相信大家也是非常熟悉的,指定了誰,誰就不能重復(fù),直接上圖。
group
在mongodb里面做group操作有點小復(fù)雜,不過大家對sql server里面的group比較熟悉的話還是一眼
能看得明白的,其實group操作本質(zhì)上形成了一種“k-v”模型,就像C#中的Dictionary,好,有了這種思維,
我們來看看如何使用group。
下面舉的例子就是按照age進行g(shù)roup操作,value為對應(yīng)age的姓名。下面對這些參數(shù)介紹一下:
key: 這個就是分組的key,我們這里是對年齡分組。
initial: 每組都分享一個”初始化函數(shù)“,特別注意:是每一組,比如這個的age=20的value的list分享一個
initial函數(shù),age=22同樣也分享一個initial函數(shù)。
$reduce: 這個函數(shù)的第一個參數(shù)是當前的文檔對象,第二個參數(shù)是上一次function操作的累計對象,第一次
為initial中的{”perosn“:[]}。有多少個文檔, $reduce就會調(diào)用多少次。
看到上面的結(jié)果,是不是有點感覺,我們通過age查看到了相應(yīng)的name人員,不過有時我們可能有如下的要求:
①:想過濾掉age>25一些人員。
②:有時person數(shù)組里面的人員太多,我想加上一個count屬性標明一下。
針對上面的需求,在group里面還是很好辦到的,因為group有這么兩個可選參數(shù): condition 和 finalize。
condition: 這個就是過濾條件。
finalize:這是個函數(shù),每一組文檔執(zhí)行完后,多會觸發(fā)此方法,那么在每組集合里面加上count也就是它的活了。
mapReduce
這玩意算是聚合函數(shù)中最復(fù)雜的了,不過復(fù)雜也好,越復(fù)雜就越靈活。
mapReduce其實是一種編程模型,用在分布式計算中,其中有一個“map”函數(shù),一個”reduce“函數(shù)。
① map:
這個稱為映射函數(shù),里面會調(diào)用emit(key,value),集合會按照你指定的key進行映射分組。
② reduce:
這個稱為簡化函數(shù),會對map分組后的數(shù)據(jù)進行分組簡化,注意:在reduce(key,value)中的key就是
emit中的key,vlaue為emit分組后的emit(value)的集合,這里也就是很多{"count":1}的數(shù)組。
③ mapReduce:
這個就是最后執(zhí)行的函數(shù)了,參數(shù)為map,reduce和一些可選參數(shù)。具體看圖可知:
從圖中我們可以看到如下信息:
result: "存放的集合名“;
input:傳入文檔的個數(shù)。
emit:此函數(shù)被調(diào)用的次數(shù)。
reduce:此函數(shù)被調(diào)用的次數(shù)。
output:最后返回文檔的個數(shù)。
最后我們看一下“collecton”集合里面按姓名分組的情況。
游標
mongodb里面的游標有點類似我們說的C#里面延遲執(zhí)行,比如:
var list=db.person.find();
針對這樣的操作,list其實并沒有獲取到person中的文檔,而是申明一個“查詢結(jié)構(gòu)”,等我們需要的時候通過
for或者next()一次性加載過來,然后讓游標逐行讀取,當我們枚舉完了之后,游標銷毀,之后我們在通過list獲取時,
發(fā)現(xiàn)沒有數(shù)據(jù)返回了。
當然我們的“查詢構(gòu)造”還可以搞得復(fù)雜點,比如分頁,排序都可以加進去。
var single=db.person.find().sort({"name",1}).skip(2).limit(2);
那么這樣的“查詢構(gòu)造”可以在我們需要執(zhí)行的時候執(zhí)行,大大提高了不必要的花銷。
索引操作
好,今天分享下mongodb中關(guān)于索引的基本操作,我們?nèi)粘W鲩_發(fā)都避免不了要對程序進行性能優(yōu)化,而程序的操作無非就是CURD,通常我們
又會花費50%的時間在R上面,因為Read操作對用戶來說是非常敏感的,處理不好就會被人唾棄,呵呵。
從算法上來說有5種經(jīng)典的查找,具體的可以參見我的算法速成系列,這其中就包括我們今天所說的“索引查找”,如果大家對sqlserver比較了解
的話,相信索引查找能給我們帶來什么樣的性能提升吧。
我們首先插入10w數(shù)據(jù),上圖說話:
一:性能分析函數(shù)(explain)
好了,數(shù)據(jù)已經(jīng)插入成功,既然我們要做分析,肯定要有分析的工具,幸好mongodb中給我們提供了一個關(guān)鍵字叫做“explain",那么怎么用呢?
還是看圖,注意,這里的name字段沒有建立任何索引,這里我就查詢一個“name10000”的姓名。
仔細看紅色區(qū)域,有幾個我們關(guān)心的key。
cursor: 這里出現(xiàn)的是”BasicCursor",什么意思呢,就是說這里的查找采用的是“表掃描”,也就是順序查找,很悲催啊。
nscanned: 這里是10w,也就是說數(shù)據(jù)庫瀏覽了10w個文檔,很恐怖吧,這樣玩的話讓人受不了啊。
n: 這里是1,也就是最終返回了1個文檔。
millis: 這個就是我們最最最....關(guān)心的東西,總共耗時114毫秒。
建立索引(ensureIndex)
在10w條這么簡單的集合中查找一個文檔要114毫秒有一點點讓人不能接受,好,那么我們該如何優(yōu)化呢?mongodb中給
我們帶來了索引查找,看看能不能讓我們的查詢一飛沖天.....
這里我們使用了ensureIndex在name上建立了索引。”1“:表示按照name進行升序,”-1“:表示按照name進行降序。
我的神啊,再來看看這些敏感信息。
cursor: 這里出現(xiàn)的是”BtreeCursor",這么牛X,mongodb采用B樹的結(jié)構(gòu)來存放索引,索引名為后面的“name_1"。
nscanned: 我數(shù)據(jù)庫只瀏覽了一個文檔就OK了。
n: 直接定位返回。
millis: 看看這個時間真的不敢相信,秒秒殺。
通過這個例子相信大家對索引也有了感官方面的認識了吧。
唯一索引
和sqlserver一樣都可以建立唯一索引,重復(fù)的鍵值自然就不能插入,在mongodb中的使用方法是:
db.person.ensureIndex({"name":1},{"unique":true})。
組合索引
有時候我們的查詢不是單條件的,可能是多條件,比如查找出生在‘1989-3-2’名字叫‘jack’的同學,那么我們可以建立“姓名”和"生日“
的聯(lián)合索引來加速查詢。
看到上圖,大家或者也知道name跟birthday的不同,建立的索引也不同,升序和降序的順序不同都會產(chǎn)生不同的索引,
那么我們可以用getindexes來查看下person集合中到底生成了哪些索引。
此時我們肯定很好奇,到底查詢優(yōu)化器會使用哪個查詢作為操作,呵呵,還是看看效果圖:
看完上圖我們要相信查詢優(yōu)化器,它給我們做出的選擇往往是最優(yōu)的,因為我們做查詢時,查詢優(yōu)化器會使用我們建立的這些索引來創(chuàng)建查詢方案,
如果某一個先執(zhí)行完則其他查詢方案被close掉,這種方案會被mongodb保存起來,當然如果非要用自己指定的查詢方案,這也是
可以的,在mongodb中給我們提供了hint方法讓我們可以暴力執(zhí)行。
刪除索引
可能隨著業(yè)務(wù)需求的變化,原先建立的索引可能沒有存在的必要了,可能有的人想說沒必要就沒必要唄,但是請記住,索引會降低CUD這三
種操作的性能,因為這玩意需要實時維護,所以啥問題都要綜合考慮一下,這里就把剛才建立的索引清空掉來演示一下:dropIndexes的使用。
mapreduce