文章目錄
注意事項
- 我們始終以下面的幾條數據爲測試用例:
"name" : "張三", "age" : "18"
"name" : "李四", "age" : "28"
"name" : "王五", "age" : "38"
- col即爲collection,是我們創建的集合,相當於mysql中的table,我們始終以
test
爲集合名
一、啓動和關閉服務
// 啓動
sudo mongod
// 關閉
mongo
use admin
db.shutdownServer()
二、數據庫的使用
2.1 連接數據庫
// 連接
mongo
// 查看所有數據庫
show dbs
2.2 登錄數據庫
// 登錄賬戶
mongodb://username:password@ip
// 例如
mongodb://admin:123456@localhost/
// 登錄賬戶並使用指定數據庫
mongodb://username:password@ip/dbname
// 例如
mongodb://admin:123456@localhost/test_db
2.3 創建/刪除/選擇數據庫
// 數據庫不存在則創建數據庫,否則切換到數據庫
use test_db
// 查看當前選擇的數據庫
db
// 查看所有數據庫
show dbs
// 刪除當前數據庫
db.dropDatabase()
三、集合collection
集合相當於mysql中的數據表(table)。
- db.createCollection(name, options)
- name: 要創建的集合名稱
- options: 可選參數, 指定有關內存大小及索引的選項
// 創建集合 test
db.createCollection("test")
db.createCollection("test", { capped : true, autoIndexId : true, size :
6142800, max : 10000 } )
// 刪除集合test
db.test.drop()
// 查看集合
show collections
options 可以是如下參數:
字段 | 類型 | 描述 |
---|---|---|
capped | 布爾 | (可選)如果爲 true,則創建固定集合。固定集合是指有着固定大小的集合,當達到最大值時,它會自動覆蓋最早的文檔。 當該值爲 true 時,必須指定 size 參數。 |
autoIndexId | 布爾 | (可選)如爲 true,自動在 _id 字段創建索引。默認爲 false。 |
size | 數值 | (可選)爲固定集合指定一個最大值(以字節計)。 如果 capped 爲 true,也需要指定該字段。 |
max | 數值 | (可選)指定固定集合中包含文檔的最大數量。 |
在 MongoDB 中,往往不需要創建集合。當插入一些文檔時,MongoDB 會自動創建集合。
db.test.insert({"name":"張三","age":18)
四、文檔document
文檔相當於mysql中數據表(table)中的行(row),對應着一條數據。
4.1 插入文檔
- db.COLLECTION_NAME.insert(document)
db.test.insert({"name":"張三","age":18})
// 或者去掉引號
db.test.insert({name:"張三",age:18})
// 也可以先聲明document
doc = {name:"王五",age:18}
db.test.insert(doc)
// 接下來我們查詢一下
db.test.find()
4.2 更新文檔
使用 update() 和 save() 方法來更新集合中的文檔。
4.2.1 update
update() 方法用於更新已存在的文檔。
-
db.db.collection.update(
query,
update,
{
upsert: boolean,
multi: boolean,
writeConcern: document
}
)參數說明:
- query : update的查詢條件,類似sql update查詢內where後面的。
- update : update的對象和一些更新的操作符(如inc…)等,也可以理解爲sql update查詢內set後面的
- upsert : 可選,這個參數的意思是,如果不存在update的記錄,是否插入objNew,true爲插入,默認是false,不插入。
- multi : 可選,mongodb 默認是false,只更新找到的第一條記錄,如果這個參數爲true,就把按條件查出來多條記錄全部更新。
- writeConcern :可選,拋出異常的級別。
Demo:
我們將李四的年齡改成28
db.test.update({name:"李四"}, {$set:{age:28}})
// 只更新找到的第一條記錄
db.test.update({name:"李四"}, {$set:{age:28}},{multi:true})
4.2.2 save
save() 方法通過傳入的文檔來替換已有文檔。
-
db.collection.save(
Document,
{
writeConcern: document
}
)參數說明:
- document : 文檔數據。
- writeConcern :可選,拋出異常的級別。
Demo:
我們先來查詢已存在的文檔
db.test.find()
我這裏打印出的是這些
{ "_id" : ObjectId("5d491fdcc76a65c99c61aa4f"), "name" : "張三", "age" : 18 } { "_id" : ObjectId("5d492019c76a65c99c61aa50"), "name" : "李四", "age" : 28 } { "_id" : ObjectId("5d49208ec76a65c99c61aa51"), "name" : "王五", "age" : 18 }
然後我們替換王五,並把年齡改成38
db.test.save({ "_id" : ObjectId("5d49208ec76a65c99c61aa51"), "name" : "王五", "age" : 38 })
王五的整條數據就都被替換了。
更多實例:
// 只更新第一條記錄 db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } ); // 全部更新 db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true ); // 只添加第一條 db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false ); // 全部添加進去 db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true ); // 全部更新 db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true ); // 只更新第一條記錄 db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );
4.2.3 補充
在3.2版本開始,MongoDB提供以下更新集合文檔的方法:
- db.collection.updateOne() 向指定集合更新單個文檔
- db.collection.updateMany() 向指定集合更新多個文檔
WriteConcern說明
- WriteConcern.NONE:沒有異常拋出
- WriteConcern.NORMAL:僅拋出網絡錯誤異常,沒有服務器錯誤異常
- WriteConcern.SAFE:拋出網絡錯誤異常、服務器錯誤異常;並等待服務器完成寫操作。
- WriteConcern.MAJORITY: 拋出網絡錯誤異常、服務器錯誤異常;並等待一個主服務器完成寫操作。
- WriteConcern.FSYNC_SAFE: 拋出網絡錯誤異常、服務器錯誤異常;寫操作等待服務器將數據刷新到磁盤。
- WriteConcern.JOURNAL_SAFE:拋出網絡錯誤異常、服務器錯誤異常;寫操作等待服務器提交到磁盤的日誌文件。
- WriteConcern.REPLICAS_SAFE:拋出網絡錯誤異常、服務器錯誤異常;等待至少2臺服務器完成寫操作。
移除集合中的鍵值對,使用的 $unset 操作符:
db.collection.update({"_id":"56064f89ade2f21f36b03136"}, {$unset:{ "test2" : "OK"}})
4.3 刪除文檔
4.3.1 remove,已過時
remove() 方法 並不會真正釋放空間。
需要繼續執行 db.repairDatabase() 來回收磁盤空間。
-
db.collection.remove(
query,
justOne
) -
如果你的 MongoDB 是 2.6 版本以後的,語法格式如下:
db.collection.remove(
query,
{
justOne: boolean,
writeConcern: document- 參數說明:
- query :(可選)刪除的文檔的條件。
- justOne : (可選)如果設爲 true 或 1,則只刪除一個文檔,如果不設置該參數,或使用默認值 false,則刪除所有匹配條件的文檔。
- writeConcern :(可選)拋出異常的級別。
- 參數說明:
Demo:
db.test.remove({'name':'王五'})
如果你想刪除所有數據
db.test.remove({})
4.3.2 deleteOne() 和 deleteMany()
這裏很簡單,對應上面的
// 刪除王五
db.test.deleteOne({'name':'王五'})
// 刪除所有叫王五的
db.test.deleteMany({'name':'王五'})
// 刪除所有數據
db.test.deleteMany({})
4.4 查詢文檔
- db.collection.find(query, projection)
- query :可選,使用查詢操作符指定查詢條件
- projection :可選,使用投影操作符指定返回的鍵。查詢時返回文檔中所有鍵值, 只需省略該參數即可(默認省略)。
- db.col.find(query, {name: 1, age: 1}) // inclusion模式 指定返回的鍵,不返回其他鍵
- db.col.find(query, {name: 0, age: 0}) // exclusion模式 指定不返回的鍵,返回其他鍵
- _id 鍵默認返回,需要主動指定 _id:0 纔會隱藏
- 兩種模式不可混用
- db.collection.find().pretty() , pretty()表示只讀查詢
示例:
// 查詢全部
db.test.find()
// 只讀查詢全部
db.test.find().pretty()
// 查詢一個
db.test.findOne()
4.4.1 MongoDB 與 RDBMS Where 語句比較
如果你熟悉常規的 SQL 數據,通過下表可以更好的理解 MongoDB 的條件語句查詢:
操作 | 格式 | 範例 | RDBMS中的類似語句 |
---|---|---|---|
等於 | {<key>:<value> } |
db.col.find({"by":"測試的啦"}).pretty() |
where by = '測試的啦' |
小於 | {<key>:{$lt:<value>}} |
db.col.find({"likes":{$lt:50}}).pretty() |
where likes < 50 |
小於或等於 | {<key>:{$lte:<value>}} |
db.col.find({"likes":{$lte:50}}).pretty() |
where likes <= 50 |
大於 | {<key>:{$gt:<value>}} |
db.col.find({"likes":{$gt:50}}).pretty() |
where likes > 50 |
大於或等於 | {<key>:{$gte:<value>}} |
db.col.find({"likes":{$gte:50}}).pretty() |
where likes >= 50 |
不等於 | {<key>:{$ne:<value>}} |
db.col.find({"likes":{$ne:50}}).pretty() |
where likes != 50 |
// 查詢年齡大於20且小於30的人
db.test.find( { age: { $gt: 20 ,$lt: 30}} )
4.4.2 And條件
- db.col.find({key1:value1, key2:value2}).pretty()
示例:
// 查詢姓名爲 李四,年齡爲 28 的人
db.test.fins({"name":"李四", "age":"28"})
4.4.3 Or條件
OR 條件語句使用了關鍵字 $or,語法格式如下:
- db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
示例:
// 查詢姓名爲 李四,或者年齡爲 18 的人
db.test.find({$or:[{"name":"李四"},{"age":"18"}]})
4.4.4 And和Or的聯合使用
// 查詢年齡爲 28,且姓名爲 張三 或者 李四 的人
db.test.find({"age":"28", $or:[{"name":"張三"}, {"name":"李四"}]})
4.4.5 type操作符
我們可以通過type操作符檢索集合中匹配的數據類型
類型 | 數字 | 備註 |
---|---|---|
Double | 1 | |
String | 2 | |
Object | 3 | |
Array | 4 | |
Binary data | 5 | |
Undefined | 6 | 已廢棄。 |
Object id | 7 | |
Boolean | 8 | |
Date | 9 | |
Null | 10 | |
Regular Expression | 11 | |
JavaScript | 13 | |
Symbol | 14 | |
JavaScript (with scope) | 15 | |
32-bit integer | 16 | |
Timestamp | 17 | |
64-bit integer | 18 | |
Min key | 255 | Query with -1 . |
Max key | 127 |
// 查詢所有 age 字段爲 String 的數據, 得到的是所有數據,因爲我們插入的age並不是int型,記着嗎,我們insert的時候是 "age":"xxx"
db.test.find({age:{$type:2}})
db.test.find({age:{$type:"string"}})
4.4.6 分頁查詢:Limit與Skip方法
skip和limit方法只適合小數據量分頁,如果是百萬級效率就會非常低,因爲skip方法是一條條數據數過去的,建議使用where_limit
4.4.6.1 Limit方法
如果你需要在MongoDB中讀取指定數量的數據記錄,可以使用MongoDB的Limit方法,limit()方法接受一個數字參數,該參數指定從MongoDB中讀取的記錄條數。
- db.col.find().limit(num)
4.4.6.2 Skip()方法
我們除了可以使用limit()方法來讀取指定數量的數據外,還可以使用skip()方法來跳過指定數量的數據,skip方法同樣接受一個數字參數作爲跳過的記錄條數。
- db.col.find().limit(num).skip(num)
// 從第10調開始讀取100條
db.col.find().skip(10).limit(100)
// 下面的會跳過 張三, 只顯示一條 李四
db.test.find().limit(1).skip(1)
4.4.7 排序
使用 sort() 方法對數據進行排序,sort() 方法可以通過參數指定排序的字段,並使用 1 和 -1 來指定排序的方式,其中 1 爲升序排列,而 -1 是用於降序排列。
- db.col.find().sort({key:1})
// 根據 age 字段降序排列,現實的順序爲 王五 李四 張三
db.test.find().sort({age:-1})
4.4.8 索引
使用 createIndex() 方法來創建索引。
注意在 3.0.0 版本前創建索引方法爲 db.collection.ensureIndex(),之後的版本使用了 db.collection.createIndex() 方法,ensureIndex() 還能用,但只是 createIndex() 的別名。
- db.collection.createIndex(keys, options)
// 1 爲指定按升序創建索引,如果你想按降序來創建索引指定爲 -1 即可
db.test.createIndex({"age":1})
createIndex() 方法中你也可以設置使用多個字段創建索引(關係型數據庫中稱作複合索引)。
db.test.createIndex({"age":1,"name":-1})
createIndex() 接收可選參數,可選參數列表如下:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引過程會阻塞其它數據庫操作,background可指定以後臺方式創建索引,即增加 “background” 可選參數。 “background” 默認值爲false。 |
unique | Boolean | 建立的索引是否唯一。指定爲true創建唯一索引。默認值爲false. |
name | string | 索引的名稱。如果未指定,MongoDB的通過連接索引的字段名和排序順序生成一個索引名稱。 |
dropDups | Boolean | **3.0+版本已廢棄。**在建立唯一索引時是否刪除重複記錄,指定 true 創建唯一索引。默認值爲 false. |
sparse | Boolean | 對文檔中不存在的字段數據不啓用索引;這個參數需要特別注意,如果設置爲true的話,在索引字段中不會查詢出不包含對應字段的文檔.。默認值爲 false. |
expireAfterSeconds | integer | 指定一個以秒爲單位的數值,完成 TTL設定,設定集合的生存時間。 |
v | index version | 索引的版本號。默認的索引版本取決於mongod創建索引時運行的版本。 |
weights | document | 索引權重值,數值在 1 到 99,999 之間,表示該索引相對於其他索引字段的得分權重。 |
default_language | string | 對於文本索引,該參數決定了停用詞及詞幹和詞器的規則的列表。 默認爲英語 |
language_override | string | 對於文本索引,該參數指定了包含在文檔中的字段名,語言覆蓋默認的language,默認值爲 language. |
// 在後臺創建索引
db.test.createIndex({name: 1, age: 1}, {background: true})
- 查看集合索引
db.col.getIndexes()
- 查看集合索引大小
db.col.totalIndexSize()
- 刪除集合所有索引
db.col.dropIndexes()
- 刪除集合指定索引
db.col.dropIndex("索引名稱")
4.4.9 聚合
聚合主要用於處理數據(諸如統計平均值,求和等),並返回計算後的數據結果。有點類似sql語句中的 count(*)。
- db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
示例:
假設我們現在有四條數據:
{ "_id" : ObjectId("5d522cd625e9232e10114e20"), "name" : "張三", "age" : 18, "like" : 100 }
{ "_id" : ObjectId("5d522cde25e9232e10114e21"), "name" : "李四", "age" : 28, "like" : 150 }
{ "_id" : ObjectId("5d522ce625e9232e10114e22"), "name" : "王五", "age" : 38, "like" : 200 }
{ "_id" : ObjectId("5d5252242825b2cef2d05dd3"), "name" : "趙六", "age" : 48, "like" : 200 }
執行:
// 以like分組,並且求like字段相同的人數總和
// 相當於 select like as _id, count(*) as total from test group by like;
db.test.aggregate([{$group: {_id:"$like", total:{$sum: 1}}}])
// log
{ "_id" : 200, "total" : 2 }
{ "_id" : 150, "total" : 1 }
{ "_id" : 100, "total" : 1 }
執行:
// 以like分組,並且求like字段相同的年齡的平均值
db.test.aggregate({$group: {_id:"$like", avg_age:{$avg:"$age"}} })
// log
{ "_id" : 200, "avg_age" : 43 }
{ "_id" : 150, "avg_age" : 28 }
{ "_id" : 100, "avg_age" : 18 }
4.4.10 管道
管道在Unix和Linux中一般用於將當前命令的輸出結果作爲下一個命令的參數。
MongoDB的聚合管道將MongoDB文檔在一個管道處理完畢後將結果傳遞給下一個管道處理。管道操作是可以重複的。
表達式:處理輸入文檔並輸出。表達式是無狀態的,只能用於計算當前聚合管道的文檔,不能處理其它的文檔。
這裏我們介紹一下聚合框架中常用的幾個操作:
- $project:修改輸入文檔的結構。可以用來重命名、增加或刪除域,也可以用於創建計算結果以及嵌套文檔。
- $match:用於過濾數據,只輸出符合條件的文檔。$match使用MongoDB的標準查詢操作。
- $limit:用來限制MongoDB聚合管道返回的文檔數。
- $skip:在聚合管道中跳過指定數量的文檔,並返回餘下的文檔。
- $unwind:將文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值。
- $group:將集合中的文檔分組,可用於統計結果。
- $sort:將輸入文檔排序後輸出。
- $geoNear:輸出接近某一地理位置的有序文檔。
4.4.10.1 $project
用來控制顯示的字段
// 下面的兩個查詢結果是相等的,都只查詢出那麼和age字段,其他的字段不會查詢
db.test.aggregate({$project:{name:1,age:1}})
db.test.find({},{like:1,age:1})
4.4.10.11 $match
// 查詢like大於100且小於200的數據
db.test.aggregate({$match:{like:{$gt:100,$lt:200}}})
// 查詢like大於100且小於200的數據,後將符合條件的記錄送到下一階段$group管道操作符進行處理(將查詢到的數據不分組,顯示總數)
db.test.aggregate([{$match:{like:{$gt:100,$lt:300}}}, {$group:{_id:null,count:{$sum:1}} }])
//log
{ "_id" : null, "count" : 3 }
// 查詢like大於100且小於200的數據,後將符合條件的記錄送到下一階段$group管道操作符進行處理(將查詢到的數據按姓名分組,顯示每組總數)
db.test.aggregate([{$match:{like:{$gt:100,$lt:300}}}, {$group:{_id:null,count:{$sum:1}} }])
//log
{ "_id" : "趙六", "count" : 1 }
{ "_id" : "王五", "count" : 1 }
{ "_id" : "李四", "count" : 1 }
4.4.10.12 $skip
db.test.aggregate({ $skip : 5 });
五、DB 複製(副本集)
MongoDB複製是將數據同步在多個服務器的過程。
複製提供了數據的冗餘備份,並在多個服務器上存儲數據副本,提高了數據的可用性, 並可以保證數據的安全性。
複製還允許您從硬件故障和服務中斷中恢復數據。
在本教程中我們使用同一個MongoDB來做MongoDB主從的實驗, 操作步驟如下:
1、關閉正在運行的MongoDB服務器。
現在我們通過指定 --replSet 選項來啓動mongoDB。–replSet 基本語法格式如下:
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
實例
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
以上實例會啓動一個名爲rs0的MongoDB實例,其端口號爲27017。
啓動後打開命令提示框並連接上mongoDB服務。
在Mongo客戶端使用命令rs.initiate()來啓動一個新的副本集。
我們可以使用rs.conf()來查看副本集的配置
查看副本集狀態使用 rs.status() 命令
副本集添加成員
添加副本集的成員,我們需要使用多臺服務器來啓動mongo服務。進入Mongo客戶端,並使用rs.add()方法來添加副本集的成員。
語法
rs.add() 命令基本語法格式如下:
>rs.add(HOST_NAME:PORT)
實例
假設你已經啓動了一個名爲mongod1.net,端口號爲27017的Mongo服務。 在客戶端命令窗口使用rs.add() 命令將其添加到副本集中,命令如下所示:
>rs.add("mongod1.net:27017")
>
MongoDB中你只能通過主節點將Mongo服務添加到副本集中, 判斷當前運行的Mongo服務是否爲主節點可以使用命令db.isMaster() 。
MongoDB的副本集與我們常見的主從有所不同,主從在主機宕機後所有服務將停止,而副本集在主機宕機後,副本會接管主節點成爲主節點,不會出現宕機的情況。
六、分片
分片
在Mongodb裏面存在另一種集羣,就是分片技術,可以滿足MongoDB數據量大量增長的需求。
當MongoDB存儲海量的數據時,一臺機器可能不足以存儲數據,也可能不足以提供可接受的讀寫吞吐量。這時,我們就可以通過在多臺機器上分割數據,使得數據庫系統能存儲和處理更多的數據。
爲什麼使用分片
- 複製所有的寫入操作到主節點
- 延遲的敏感數據會在主節點查詢
- 單個副本集限制在12個節點
- 當請求量巨大時會出現內存不足。
- 本地磁盤不足
- 垂直擴展價格昂貴
MongoDB分片
下圖展示了在MongoDB中使用分片集羣結構分佈:
上圖中主要有如下所述三個主要組件:
-
Shard:
用於存儲實際的數據塊,實際生產環境中一個shard server角色可由幾臺機器組個一個replica set承擔,防止主機單點故障
-
Config Server:
mongod實例,存儲了整個 ClusterMetadata,其中包括 chunk信息。
-
Query Routers:
前端路由,客戶端由此接入,且讓整個集羣看上去像單一數據庫,前端應用可以透明使用。