什么是MongoDB?
mongodb是一個基于分布式文件儲存的數據庫,由C++編寫。是一個文檔型數據庫,提供好的性能,領先的非關系型數據庫
MongoDB的儲存形式類似于Python的字典,以{‘key’:‘value’}的形式儲存
mongoDB適用于哪些場景?
1.網站數據量大
2,網站數據讀寫操作頻繁
3.價值較低
數據庫MySQL和mongdb的區別?
SQL:mysql、Oracle、sqlserver、db2
- 高度事務性場景:銀行、會計、貿易,庫管,需要大量原子性操作
- 適合存儲結構化數據,如用戶的帳號、地址,預先定義明確的字段
- 數據價值高、對安全性要求高、穩定性要求高
- 需要持久化存儲的 "冷數據"(不需要經常讀寫的數據)
- 需要通過SQL語言做關聯查詢,比如join
6.這些數據的規模、增長的速度通常是可以預期的
NoSQL:
redis key:value(string、hash、set、zset、list)、
mongodb {"name" : "xiaoming", "age" : 18}, {}
mongodb:字典格式,支持分組、索引、主從備份、集群
- 靈活的數據結構,適合存儲非結構化數據,如文章、評論,需要事先設計 數據的增刪改 的字段
- 高度收縮性場景,社交網絡、熱點資訊,NoSQL數據庫通常具有無限(至少接近)伸縮性
- 處理 "熱數據"(經常需要讀寫的數據),這些數據通常用于模糊處理,如全文搜索、機器學習
- 這些數據是海量的,而且增長的速度是難以預期的,更容易擴展
- 按key獲取數據效率很高,但是對join或其他結構化查詢的支持就比較差
大型互聯網項目都會選用MySQL(或任何關系型數據庫) + NoSQL的組合方案
端口號:
mysql: 3306
redis: 6379
mongodb: 27017
mongodb安裝教程
關于mongodb的好處,優點之類的這里就不說了,唯一要講的一點就是mongodb中有三元素:數據庫,集合,文檔,其中“集合”
就是對應關系數據庫中的“表”,“文檔”對應“行”。
下載
上MongoDB官網 ,我們發現有32bit和64bit,這個就要看你系統了,不過這里有兩點注意:
①:根據業界規則,偶數為“穩定版”(如:1.6.X,1.8.X),奇數為“開發版”(如:1.7.X,1.9.X),這兩個版本的區別相信大家都知道吧。
②:32bit的mongodb最大只能存放2G的數據,64bit就沒有限制。
我這里就下載"2.0.2版本,32bit“,ok,下載之后我就放到”E盤“,改下文件夾名字為”mongodb“。
啟動
①:啟動之前,我們要給mongodb指定一個文件夾,這里取名為”db",用來存放mongodb的數據。

②:微軟徽標+R,輸入cmd,首先找到“mongodb”的路徑,然后運行mongod開啟命令,同時用--dbpath指定數據存放地點為“db”文件夾。

③:最后要看下是否開啟成功,從圖中的信息中獲知,mongodb采用27017端口,那么我們就在瀏覽器里面鍵入“http://localhost:27017/”,
打開后,mongodb告訴我們在27017上Add 1000可以用http模式查看mongodb的管理信息。

基本操作
由于是開篇,就大概地說下基本的“增刪查改“,我們再開一個cmd,輸入mongo命令打開shell,其實這個shell就是mongodb的客戶端,
同時也是一個js的編譯器,默認連接的是“test”數據庫。

insert 操作
好,數據庫有了,下一步就是集合,這里就取集合名為“person”,要注意的就是文檔是一個json的擴展(Bson)形式。

find 操作
我們將數據插入后,肯定是要find出來,不然插了也白插,這里要注意兩點:
① “_id": 這個字段是數據庫默認給我們加的GUID,目的就是保證數據的唯一性。
② 嚴格的按照Bson的形式書寫文檔,不過也沒關系,錯誤提示還是很強大的。

update操作
update方法的第一個參數為“查找的條件”,第二個參數為“更新的值”,學過C#,相信還是很好理解的。

remove操作
remove中如果不帶參數將刪除所有數據,呵呵,很危險的操作,在mongodb中是一個不可撤回的操作,三思而后行。

細說增刪改查
有一天當我們用上一篇同樣的方式打開mongodb,突然
傻眼了,擦,竟然開啟不了,仔細觀察“劃線區域“的信息,發現db文件夾下有一個類似的”lock file”阻止了mongodb的開啟,接下來我們要做的就
是干掉它,之后,開啟成功,關于mongodb的管理方式將在后續文章分享。

一: Insert操作
上一篇也說過,文檔是采用“K-V”格式存儲的,如果大家對JSON比較熟悉的話,我相信學mongodb是手到擒來,我們知道JSON里面Value
可能是“字符串”,可能是“數組”,又有可能是內嵌的一個JSON對象,相同的方式也適合于BSON。
常見的插入操作也就兩種形式存在:“單條插入”和“批量插入”。
① 單條插入
先前也說了,mongo命令打開的是一個JAVAscript shell。所以js的語法在這里面都行得通,看起來是不是很牛X。

② 批量插入
這玩意跟“單條插入”的差異相信大家應該知道,由于mongodb中沒有提供給shell的“批量插入方法”,沒關系,各個語言的driver都打通
了解mongodb內部的批量插入方法,因為該方法是不可或缺的,如果大家非要模擬下批量插入的話,可以自己寫了for循環,里面就是insert。
二:Find操作
日常開發中,我們玩查詢,玩得最多的也就是二類:
①: >, >=, <, <=, !=, =。
②:And,OR,In,NotIn
這些操作在mongodb里面都封裝好了,下面就一一介紹:
<1>"gt", "gt","gte", "lt", "lt","lte", "$ne", "沒有特殊關鍵字",這些跟上面是一一對應的,舉幾個例子。

<2> "無關鍵字“, "or", "or","in","$nin" 同樣我也是舉幾個例子

<3> 在mongodb中還有一個特殊的匹配,那就是“正則表達式”,這玩意威力很強的。

<4> 有時查詢很復雜,很蛋疼,不過沒關系,mongodb給我們祭出了大招,它就是where,為什么這么說,是因為where,為什么這么說,是因為where中的value
就是我們非常熟悉,非常熱愛的js來助我們一馬平川。

三:Update操作
更新操作無非也就兩種,整體更新和局部更新,使用場合相信大家也清楚。
<1> 整體更新
不知道大家可還記得,我在上一篇使用update的時候,其實那種update是屬于整體更新。

<2> 局部更新
有時候我們僅僅需要更新一個字段,而不是整體更新,那么我們該如何做呢?easy的問題,mongodb中已經給我們提供了兩個
修改器: inc 和inc和set。
① $inc修改器
$inc也就是increase的縮寫,學過sql server 的同學應該很熟悉,比如我們做一個在線用戶狀態記錄,每次修改會在原有的基礎上
自增$inc指定的值,如果“文檔”中沒有此key,則會創建key,下面的例子一看就懂。

② $set修改器
啥也不說了,直接上代碼

<3> upsert操作
這個可是mongodb創造出來的“詞”,大家還記得update方法的第一次參數是“查詢條件”嗎?,那么這個upsert操作就是說:如果我
沒有查到,我就在數據庫里面新增一條,其實這樣也有好處,就是避免了我在數據庫里面判斷是update還是add操作,使用起來很簡單
將update的第三個參數設為true即可。

<4> 批量更新
在mongodb中如果匹配多條,默認的情況下只更新第一條,那么如果我們有需求必須批量更新,那么在mongodb中實現也是很簡單
的,在update的第四個參數中設為true即可。例子就不舉了。
四: Remove操作
這個操作在上一篇簡單的說過,這里就不贅述了。
細說高級操作
今天跟大家分享一下mongodb中比較好玩的知識,主要包括:聚合,游標。
一: 聚合
常見的聚合操作跟sql server一樣,有:count,distinct,group,mapReduce。
count
count是最簡單,最容易,也是最常用的聚合工具,它的使用跟我們C#里面的count使用簡直一模一樣。

distinct
這個操作相信大家也是非常熟悉的,指定了誰,誰就不能重復,直接上圖。

group
在mongodb里面做group操作有點小復雜,不過大家對sql server里面的group比較熟悉的話還是一眼
能看得明白的,其實group操作本質上形成了一種“k-v”模型,就像C#中的Dictionary,好,有了這種思維,
我們來看看如何使用group。
下面舉的例子就是按照age進行group操作,value為對應age的姓名。下面對這些參數介紹一下:
key: 這個就是分組的key,我們這里是對年齡分組。
initial: 每組都分享一個”初始化函數“,特別注意:是每一組,比如這個的age=20的value的list分享一個
initial函數,age=22同樣也分享一個initial函數。
$reduce: 這個函數的第一個參數是當前的文檔對象,第二個參數是上一次function操作的累計對象,第一次
為initial中的{”perosn“:[]}。有多少個文檔, $reduce就會調用多少次。

看到上面的結果,是不是有點感覺,我們通過age查看到了相應的name人員,不過有時我們可能有如下的要求:
①:想過濾掉age>25一些人員。
②:有時person數組里面的人員太多,我想加上一個count屬性標明一下。
針對上面的需求,在group里面還是很好辦到的,因為group有這么兩個可選參數: condition 和 finalize。
condition: 這個就是過濾條件。
finalize:這是個函數,每一組文檔執行完后,多會觸發此方法,那么在每組集合里面加上count也就是它的活了。

mapReduce
這玩意算是聚合函數中最復雜的了,不過復雜也好,越復雜就越靈活。
mapReduce其實是一種編程模型,用在分布式計算中,其中有一個“map”函數,一個”reduce“函數。
① map:
這個稱為映射函數,里面會調用emit(key,value),集合會按照你指定的key進行映射分組。
② reduce:
這個稱為簡化函數,會對map分組后的數據進行分組簡化,注意:在reduce(key,value)中的key就是
emit中的key,vlaue為emit分組后的emit(value)的集合,這里也就是很多{"count":1}的數組。
③ mapReduce:
這個就是最后執行的函數了,參數為map,reduce和一些可選參數。具體看圖可知:

從圖中我們可以看到如下信息:
result: "存放的集合名“;
input:傳入文檔的個數。
emit:此函數被調用的次數。
reduce:此函數被調用的次數。
output:最后返回文檔的個數。
最后我們看一下“collecton”集合里面按姓名分組的情況。

游標
mongodb里面的游標有點類似我們說的C#里面延遲執行,比如:
var list=db.person.find();
針對這樣的操作,list其實并沒有獲取到person中的文檔,而是申明一個“查詢結構”,等我們需要的時候通過
for或者next()一次性加載過來,然后讓游標逐行讀取,當我們枚舉完了之后,游標銷毀,之后我們在通過list獲取時,
發現沒有數據返回了。

當然我們的“查詢構造”還可以搞得復雜點,比如分頁,排序都可以加進去。
var single=db.person.find().sort({"name",1}).skip(2).limit(2);
那么這樣的“查詢構造”可以在我們需要執行的時候執行,大大提高了不必要的花銷。

索引操作
好,今天分享下mongodb中關于索引的基本操作,我們日常做開發都避免不了要對程序進行性能優化,而程序的操作無非就是CURD,通常我們
又會花費50%的時間在R上面,因為Read操作對用戶來說是非常敏感的,處理不好就會被人唾棄,呵呵。
從算法上來說有5種經典的查找,具體的可以參見我的算法速成系列,這其中就包括我們今天所說的“索引查找”,如果大家對sqlserver比較了解
的話,相信索引查找能給我們帶來什么樣的性能提升吧。
我們首先插入10w數據,上圖說話:

一:性能分析函數(explain)
好了,數據已經插入成功,既然我們要做分析,肯定要有分析的工具,幸好mongodb中給我們提供了一個關鍵字叫做“explain",那么怎么用呢?
還是看圖,注意,這里的name字段沒有建立任何索引,這里我就查詢一個“name10000”的姓名。

仔細看紅色區域,有幾個我們關心的key。
cursor: 這里出現的是”BasicCursor",什么意思呢,就是說這里的查找采用的是“表掃描”,也就是順序查找,很悲催啊。
nscanned: 這里是10w,也就是說數據庫瀏覽了10w個文檔,很恐怖吧,這樣玩的話讓人受不了啊。
n: 這里是1,也就是最終返回了1個文檔。
millis: 這個就是我們最最最....關心的東西,總共耗時114毫秒。
建立索引(ensureIndex)
在10w條這么簡單的集合中查找一個文檔要114毫秒有一點點讓人不能接受,好,那么我們該如何優化呢?mongodb中給
我們帶來了索引查找,看看能不能讓我們的查詢一飛沖天.....

這里我們使用了ensureIndex在name上建立了索引。”1“:表示按照name進行升序,”-1“:表示按照name進行降序。
我的神啊,再來看看這些敏感信息。
cursor: 這里出現的是”BtreeCursor",這么牛X,mongodb采用B樹的結構來存放索引,索引名為后面的“name_1"。
nscanned: 我數據庫只瀏覽了一個文檔就OK了。
n: 直接定位返回。
millis: 看看這個時間真的不敢相信,秒秒殺。
通過這個例子相信大家對索引也有了感官方面的認識了吧。
唯一索引
和sqlserver一樣都可以建立唯一索引,重復的鍵值自然就不能插入,在mongodb中的使用方法是:
db.person.ensureIndex({"name":1},{"unique":true})。

組合索引
有時候我們的查詢不是單條件的,可能是多條件,比如查找出生在‘1989-3-2’名字叫‘jack’的同學,那么我們可以建立“姓名”和"生日“
的聯合索引來加速查詢。

看到上圖,大家或者也知道name跟birthday的不同,建立的索引也不同,升序和降序的順序不同都會產生不同的索引,
那么我們可以用getindexes來查看下person集合中到底生成了哪些索引。

此時我們肯定很好奇,到底查詢優化器會使用哪個查詢作為操作,呵呵,還是看看效果圖:

看完上圖我們要相信查詢優化器,它給我們做出的選擇往往是最優的,因為我們做查詢時,查詢優化器會使用我們建立的這些索引來創建查詢方案,
如果某一個先執行完則其他查詢方案被close掉,這種方案會被mongodb保存起來,當然如果非要用自己指定的查詢方案,這也是
可以的,在mongodb中給我們提供了hint方法讓我們可以暴力執行。

刪除索引
可能隨著業務需求的變化,原先建立的索引可能沒有存在的必要了,可能有的人想說沒必要就沒必要唄,但是請記住,索引會降低CUD這三
種操作的性能,因為這玩意需要實時維護,所以啥問題都要綜合考慮一下,這里就把剛才建立的索引清空掉來演示一下:dropIndexes的使用。

mapreduce