Mongodb—入門(介紹、安裝、操作)
1、mongodb是什麼
mongodb是一個高效能的、開源的、無模式的文件型資料庫,使用C++語言開發
隨著業務領域的發展,資料的擴大,逐漸網際網路開發演變成了超大規模和高併發模型,這樣在傳統的資料庫領域就顯得力不從心了 |
2、mongodb的特點
1、面向集合
我們在使用關係型資料也好,非關係型資料也好,往往我們的資料都是儲存在一個叫做表的單位中,但是mongodb是將資料分組儲存在集合中,我們也可以理解成:mongodb中的集合就是類似關係型資料庫中的表 |
2、模式自由
在關係型資料庫中,我們插入資料都是要按照表的結構去插入,哪怕沒有這個欄位,仍然需要在這一行中顯示一個null |
3、文件型
mongodb中儲存的資料是鍵值對的集合,鍵是字串,值可以是任何的型別 這種格式可以進行高效的二進位制資料儲存和,甚至可以進行儲存超大的物件(比如我們有一些視訊需要去儲存,使用mongodb時完全可以的) mongodb的資料邏輯結構:文件+集合+資料庫 = mongodb的邏輯結構 |
然後我們在和關係型資料庫在邏輯結構的對比
3、mongodb的內部結構
在MongoDB中,文件是對資料的抽象,它被使用在Client端和Server端的互動中。所有的Client端(各種語言的Driver)都會使用這種抽象,它的表現形式就是我們常說的BSON(Binary JSON )。
BSON是一個輕量級的二進位制資料格式。MongoDB能夠使用BSON,並將BSON作為資料的儲存存放在磁碟中。
當Client端要將寫入文件,使用查詢等等操作時,需要將文件編碼為BSON格式,然後再發送給Server端。同樣,Server端的返回結果也是編碼為BSON格式再放回給Client端的。
4、mongodb的安裝
1、curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz |
5、mongodb啟動服務端
1、在啟動服務的時候一定要預選建立好mongodb存放資料的目錄以及日誌檔案存放的目錄: 2、bin/mongod --dbpath /path/to/database --logpath /path/to/log --fork --port 27017 引數解釋: --dbpath 資料儲存目錄 --logpath 日誌儲存目錄 --port 執行埠(預設27017) --fork 後臺程序執行 |
因為每次都需要啟動服務,並且傳入需要的引數,為了方便,我們可以自己寫一個簡單的shell指令碼來更方便的啟動
#! /bin/bash clear if [ ! -n "$1" ]; then echo "============[請輸入埠號!]================" exit fi soft_path=/export/servers/mongodb echo "========================啟動服務,輸入的埠號為:$1 ===============================" $soft_path/bin/mongod --dbpath $soft_path/data_path/ --logpath $soft_path/log_path/server.log --fork --port $1 echo "========================================================================" echo "ps -ef | grep mongodb:" ps -ef | grep mongodb |
6、mongodb客戶端的登入
Bin/mongo |
7、DDL操作
建立資料庫
1、檢視當前的mongodb中有哪些資料庫:
show dbs |
2、使用db可以檢視當前使用的是哪個庫
db |
其實我們可以使用help命令來幫助我們,當我們不知道有哪些具體操作的時候,我們可以使用db.help()
3、使用use dbname來進行切換資料庫或者建立資料庫,系統就會延遲建立資料庫
只有當我們在當前的資料庫中真正的插入了某些東西以後,才可以顯示出我們剛才use的資料庫
use dbname |
我們接下來可以使用db來檢視當前使用的資料:
db course1
然後使用show dbs檢視現在有哪些的資料庫:
show dbs |
這時候系統不會立馬建立資料庫,而是等到我們後面對資料庫有了操作之後才會進行建立,是一種延遲的載入機制,很類似spark裡面的rdd延遲載入
我們也可以嘗試在新建的庫中新增一個collection,這樣我們在檢視是否有庫顯示的建立
db.storeCollection.save({name:'zhangsan',age:18,country:'china'}) WriteResult({ "nInserted" : 1 }) |
然後我們在使用show dbs就可以檢視到; show dbs;
刪除資料庫
直接使用db.dropDatabase()即可刪除資料庫
db.dropDatabase() |
建立集合
顯示的建立
createCollection()命令的基本語法如下: |
引數 | 型別 | 描述 |
name | String | 要建立的集合的名稱 |
options | Document | (可選)指定有關記憶體大小和索引的選項 |
options引數是可選的,因此只需要指定集合的名稱。以下是可以使用的選項列表:
欄位 | 型別 | 描述 |
capped | Boolean | (可選)如果為true,則啟用封閉的集合。上限集合是固定大小的集合,它在達到其最大大小時自動覆蓋其最舊的條目。如果指定true,則還需要指定size引數。 |
autoIndexId | Boolean | (可選)如果為true,則在_id欄位上自動建立索引。預設值為false。 |
size | 數字 | (可選)指定上限集合的最大大小(以位元組為單位)。如果capped為true,那麼還需要指定此欄位的值。 |
max | 數字 | (可選)指定上限集合中允許的最大文件數。 |
在插入文件時,MongoDB首先檢查上限集合capped欄位的大小,然後檢查max欄位
例項:
建立集合
db.createCollection("mycol", {capped : true, autoIndexId : true, size : 6142800, max : 10000 }) |
使用show collections來檢視:
show collections 其中的system.index是儲存索引的 |
隱式的建立
db.mycol2.insert({type:"news",title:"yule"}) db.mycol2.insert({name:"lisi",age:18}) |
通過查詢的方式進行驗證
db.mycol2.find() |
當我們建立完集合之後,我們可以去磁碟上看下,是否把資料刷到了磁碟。這樣我們下次登入還可以繼續使用上一次的資料
同樣,對於集合的操作,當我們不知道具體的命令操作的時候,我們可以使用db.database_name.coll_name來進行提示
刪除集合
db.mycol.drop() |
8、CURD操作
1、插入
Mongodb給我們提供了像一個集合中插入資料的方法:
db.collection.insertOne() db.collection.insertMany() |
這兩個方法都是在3.2版本中出現的
然後我們在看下如何進行插入操作
單挑插入:
db.inventory.insertOne( { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } } ) 然後查詢驗證: db.inventory.find() |
多條插入:
db.inventory.insertMany([ { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } }, { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } }, { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } } ]) 然後查詢驗證: db.inventory.find() |
在2.6老版本中更新的命令:
db.products.insert( { item: "card", qty: 15 } ) |
這個老版本的insert語句有一些引數可以提供我們進行選擇
引數 | 型別 | 描述 |
document | 文件或陣列 | 要插入到集合中的文件或一系列文件 |
writeConcern | 檔案 | writeConcern包括:w、j、timeout操作 |
ordered | 布林 | 可選的。如果true對陣列中的文件執行有序的插入操作,並且如果其中一個文件發生錯誤,則MongoDB將返回而不處理陣列中剩餘的文件。 如果false執行無序插入,並且某個文件發生錯誤,則繼續處理陣列中的其餘文件。 預設為true。 |
writeConcern:
MongoDB支援的WriteConncern選項如下 w: 資料寫入到number個節點才向用客戶端確認 {w: 0} 對客戶端的寫入不需要傳送任何確認,適用於效能要求高,但不關注正確性的場景 {w: 1} 預設的writeConcern,資料寫入到Primary就向客戶端傳送確認 {w: “majority”} 資料寫入到副本集大多數成員後向客戶端傳送確認,適用於對資料安全性要求比較高的場景,該選項會降低寫入效能 j: 寫入操作的journal持久化後才向客戶端確認 預設為”{j: false},如果要求Primary寫入持久化了才向客戶端確認,則指定該選項為true wtimeout: 寫入超時時間,僅w的值大於1時有效。 當指定{w: }時,資料需要成功寫入number個節點才算成功,如果寫入過程中有節點故障,可能導致這個條件一直不能滿足,從而一直不能向客戶端傳送確認結果,針對這種情況,客戶端可設定wtimeout選項來指定超時時間,當寫入過程持續超過該時間仍未結束,則認為寫入失敗。 |
例子:
db.products.insert( { item: "envelopes", qty : 100, type: "Clasp" }, { writeConcern: { w: "majority", wtimeout: 5000 } } ) 【注意:】另外當我們插入單條文件的時候,我們會發現執行成功之後是會返回給我們一個WriteResult 物件的 |
注意:
1、在mongodb中,_id是一個唯一的欄位,並且是個主鍵;我們可以不寫,系統會自動生成
2、mongodb中所有的單挑操作其實都是原子性的,要麼成功,要麼不成功
2、刪除文件
首先新增資料:
db.inventory.insertMany( [ { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" }, { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }, ]); |
刪除一條資料
db.mycol1.remove({fild1:0}) 刪除fild1為0的操作 |
刪除全部資料
db.inventory.remove({}) |
按條件刪除所有的資料
當然在3.2版本出來之後,更新了一些刪除的新命令:
比如:
刪除一條資料:
db.mycol1.deleteOne({_id:1}) |
同樣可以刪除多條的資料:
db.mycol1.deleteMany({status:"A"}) |
這條命令的意思是說,把匹配到的資料全部刪除
3、更改文件
在mongodb的老版本中,更改文件的操作是
db.collection.update(criteria,objNew,upsert,multi)
引數說明:
criteria:用於設定查詢條件的物件
objNew:用於設定更新內容的物件
upsert:如果記錄已經存在,更新它,否則新增一個記錄,取值為true或者false
multi:如果有多個符合條件的記錄,是否全部更新,取值為true或者false
注意:預設情況下,只會更新第一個符合條件的記錄
一般情況下後兩個引數分別為0,1 ,即:
db.collection.update(criteria,objNew,0,1)
例子:
> db.classes.insert({"name":"c1","count":30}) > db.classes.insert({"name":"c2","count":30}) > db.classes.find() 示例1:把count大於20的class name修改為c3 > db.classes.update({"count":{$gt:20}},{$set:{"name":"c3"}}) > db.classes.find() 上面的操作中我們把upsert和multi全部設定成了false,那麼只會更改符合條件的一條記錄 示例2:把count大於20的class name修改為c4,設定multi為true > db.classes.update({"count":{$gt:20}},{$set:{"name":"c4"}},false,true) > db.classes.find() 列2中,我們把multi設定成了true,那麼符合條件的全部被更改 示例3: 把count大於50的class name修改為c5,設定upsert為true > db.classes.update({"count":{$gt:50}},{$set:{"name":"c5"}},true,false) > db.classes.find() 列3中,把upsert為true,因為資料中沒有count大於50的,所以會新建立一條資料 |
Mongodb在3.6版本中為我們提供了這樣的方法:
db.collection.updateOne() db.collection.updateMany() db.collection.replaceOne() |
首先插入一部分資料:
db.inventory.insertMany( [ { item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" }, { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, { item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" }, { item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" }, { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" }, { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }, { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" } ]); |
3、開始進行更改操作:
(1):更新單個文件
db.inventory.updateOne( { item: "paper" }, { $set: { "size.uom": "cm", status: "P" }, $currentDate: { lastModified: true } } ) 我們使用 $set來更改item中為paper的欄位 其中符合欄位size下面有一個uom,我們改成cm; 然後status改成p |
(2):更改多個文件
db.inventory.updateMany( { "qty": { $lt: 50 } }, { $set: { "size.uom": "in", status: "P" }, $currentDate: { lastModified: true } } ) 把其中的qty欄位小於50的操作,全部更改 |
(3):替換文件
替換文件是除了_id外,所有的欄位都是可以進行替換的,甚至替換文件可以具有與原始文件不同的欄位;
db.inventory.replaceOne( { item: "paper" }, { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] } ) |
4、批量操作
在上面的操作中我們已經展示了:insertMany的批量插入方法
但是:MongoDB也為客戶提供其他批量執行寫操作的能力;
db.collection.bulkWrite()方法提供了執行批量插入,更新和刪除操作的功能
其中對於寫入的操作,mongodb支援批量的有序插入和無序插入
當我們使用有序插入的過程中,如果期間出現了異常,那麼mongodb會停止剩下的插入操作
但是當我們使用無序的插入,即便出了問題,仍然會繼續執行後面的操作
從效能角度分析,有序的插入,在操作執行時間上要比無序的慢很多,因為有有序的操作必須要等待上一個執行完畢;那麼系統預設在我們進行批量操作的時候是有序的操作
首先我們先插入三條資料:
db.characters.insertMany([ { "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 }, { "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 }, { "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 } ]) |
然後我們在使用批量的有序插入:
db.characters.bulkWrite( [ { insertOne : { "document" : { "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4 } } }, { insertOne : { "document" : { "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3 } } }, { updateOne : { "filter" : { "char" : "Eldon" }, "update" : { $set : { "status" : "Critical Injury" } } } }, { deleteOne : { "filter" : { "char" : "Brisbane"} } }, { replaceOne : { "filter" : { "char" : "Meldane" }, "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 } } } ] ); |
批量的更改操作:
首先插入資料:
db.enemies.insertMany([ { "_id" : 1, "char" : "goblin", "rating" : 1, "encounter" : 0.24 }, { "_id" : 2, "char" : "hobgoblin", "rating" : 1.5, "encounter" : 0.30 }, { "_id" : 3, "char" : "ogre", "rating" : 3, "encounter" : 0.2 }, { "_id" : 4, "char" : "ogre berserker" , "rating" : 3.5, "encounter" : 0.12} ]) |
然後進行批量的更改:
db.enemies.bulkWrite( [ { updateMany : { "filter" : { "rating" : { $gte : 3} }, "update" : { $inc : { "encounter" : 0.1 } } }, }, { updateMany : { "filter" : { "rating" : { $lt : 2} }, "update" : { $inc : { "encounter" : -0.25 } } }, }, { insertOne : { "document" : { "_id" :5, "char" : "ogrekin" , "rating" : 2, "encounter" : 0.31 } } } ], { writeConcern : { w : "majority", wtimeout : 100 } } ); 在這裡,我們進行邏輯上的判斷,然後進行批量的更改操作 |
5、查詢操作:
建立集合
db.inventory.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
1、查詢所有資料
db.inventory.find( {} ) |
2、按條件查詢
db.inventory.find( { status: "D" } ) |
3、$in的操作
如果我們想查詢status帶有A的 或者 帶有D的: db.inventory.find( { status: { $in: [ "A", "D" ] } } ) |
4、and操作
想查詢status:A 並且qty<30的 db.inventory.find( { status: "A", qty: { $lt: 30 } } ) |
5、or的操作
假如我們想查詢status:A 或者 qty<30的資料 db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } ) |
6、多條件組合查詢
在讓我們來一個稍微複雜點的操作: 我們此時想查詢:status=A並且qty < 30 或者item中以p為開頭的 db.inventory.find( { status: "A", $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ] } ) |
7、巢狀的查詢方式
首先插入資料: db.inventory.insertMany( [ { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" }, { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" } ]); 例子1:查詢size: { h: 14, w: 21, uom: "cm" }這一條資料 db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } ) 需要注意的是這個查詢內部結構的順序,如果順序不對是查詢不出來的,比如: db.inventory.find( { size: { w: 21, h: 14, uom: "cm" } } ) 列子2:帶點符號的巢狀查詢 db.inventory.find( { "size.uom": "in" } ) : |
8、查詢陣列的方法
1、插入資料 db.inventory.insertMany([ { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ]); 列子1:查詢欄位tags 值是包含兩個元素"blank", "red" 的陣列的所有文件(順序必須一致) db.inventory.find( { tags: ["red", "blank"] } ) 列子2:查詢欄位tags 值是包含兩個元素"blank", "red" 的陣列的所有文件(不考慮順序) db.inventory.find( { tags: { $all: ["red", "blank"] } } ) 【$all , 意思是隻要包含了red和blank 不管順序是什麼樣的】 列子3:查詢所有文章中dim_cm陣列的第二個引數大於25的所有文件 db.inventory.find( { "dim_cm.1": { $gt: 25 } } ) 列子4:查詢tags陣列長度大於3的所有文章 db.inventory.find( { "tags": { $size: 3 } } ) |
9、查詢null或者丟失的欄位
首先插入資料: db.inventory.insertMany([ { _id: 1, item: null }, { _id: 2 } ]) 然後: db.inventory.find( { item: null } ) 然後我們會發現查詢出了所有的欄位,這是因為itrm:null會查詢出匹配到的null值以及不包含item欄位的文章 |
練習:
上面講了不少的內容,接下來需要我們做一個練習,然後藉著練習,我們講解一下各種識別符號的含義:
1、準備資料
db.student.insertMany( [{ name:'jack', age:22, sex:'Man', tags:['python','c++','c'], grades:[22,33,44,55], school:{ name:'shida', city:'xuzhou' } },{ name:'jhon', age:33, sex:null, tags:['python','java'], grades:[66,22,44,88], school:{ name:'kuangda', city:'xuzhou' } }, { name:'xiaoming', age:33, tags:['python','java'], grades:[66,22,44,88], school:{ name:'kuangda', city:'xuzhou' } } ] ) |
我們插入3條studen他的資料
2、下面找出滿足name為jack的資料,並且只輸出name,age
(這裡的_id是預設輸出的,其他的欄位如果不想輸出將將它設定為0,想要輸出那個欄位將它設定為1)
db.student.find({name:'jack'},{name:1,age:1}) |
3、下面查詢年齡在20-30之間的資訊
db.student.find({ age:{$gt:20,$lt:30} }) |
4、查詢年齡不等於22歲的資訊
db.student.find({age:{$ne:22}}) $ne 是not equal的縮寫,意思是說不等於 |
5、查詢所有人grades陣列中的前兩個數
db.student.find({},{grades:{$slice:2},name:1,age:1,'school.name':1}); $slice操作符控制查詢返回的陣列中元素的個數 語法:db.collection.find( { field: value }, { array: {$slice: count }}); |
6、查詢所有人grades陣列中的後三個以及姓名
db.student.find({},{grades:{$slice:-3},name:1}); |
7、查詢所有的grades陣列中第二個和第三個的人
db.student.find({},{grades:{$slice:[1,2]},name:1}) 其中的$slice中第一個引數代表跳過的數目 , 第二個代表返回的數目 |
8、查詢所有文件中不包含sex的
db.student.find({sex:{$exists:false}}) 這裡我們使用了$exists , 這個欄位為true代表包含這個欄位的文件,flase代表不包含這個欄位的文件 |
9、查詢age等於22或者age等於33的值
db.student.find({$or:[{age:22},{age:33}]}) $or 代表至少滿足其中一個的 |
10、查找出年齡為22或者33並且姓名為jack的人的資訊
db.student.find({name:'jack',$or:[{age:33},{age:22}]}) |
11、查詢年齡在20--30歲之間的資訊
db.student.find({$and:[{age:{$gt:20}},{age:{$lt:30}}]}) $and是一個短路操作,表示並且 |
12、查詢年齡是22或者30歲的資訊
db.student.find({grades:{$in:[22,33]}}) $in和SQL中的in操作很類似,表示其中任意的一個 |
13、查詢出grades中不存在100或者44的文件
db.student.find({} , {grades:{$nin:[100,44]},name:1}) $nin 表示不存在 |
14、查詢年齡不大於30的資訊
db.student.find({age:{$not:{$gt:30}}}) 其中的$not操作符不能獨立使用,必須跟其他操作一起使用,表示不 |