mongoDB 學習筆記純干貨(mongoose、增刪改查、聚合、索引、連接、備份與恢復、監控等等)

最后更新時間:2017-07-13 11:10:49

原始文章鏈接:http://www.lovebxm.com/2017/07/13/mongodb_primer/

MongoDB - 簡介

官網:https://www.mongodb.com/

MongoDB 是一個基于分布式文件存儲的數據庫,由 C++ 語言編寫,旨在為 WEB 應用提供可擴展的高性能數據存儲解決方案。

MongoDB 是一個介于關系數據庫和非關系數據庫之間的產品,是非關系數據庫當中功能最豐富,最像關系數據庫的。

MongoDB - 安裝及運行

  1. 下載

07/05/2017 Current Stable Release (3.4.6)

https://www.mongodb.com/download-center#community

  1. 創建數據目錄

MongoDB 將數據目錄存儲在 db 目錄下,需手動創建。

E:\MongoDB\data\db
  1. 運行 MongoDB 服務器

為了從命令提示符下運行MongoDB服務器,你必須從MongoDB\bin目錄中執行mongod.exe文件,不要關閉服務。ctrl + c關閉。

mongod.exe --dbpath E:\MongoDB\data\db
  1. MongoDB 后臺管理

運行 mongo.exe

MongoDB Shell是MongoDB自帶的交互式Javascript shell,用來對MongoDB進行操作和管理的交互式環境。

  1. 將 MongoDB 服務器作為 Windows 服務運行

添加系統環境 path E:\MongoDB\Server\3.4\bin

檢測:cmd 中輸入 mongod --help

新建文件:E:\MongoDB\logs\logs.log

將 MongoDB 服務器作為 Windows 服務隨 Windows 啟動而開啟:

mongod.exe --logpath "E:\MongoDB\logs\logs.log" --logappend --dbpath "E:\MongoDB\data" --directoryperdb --serviceName MongoDB --install

開啟 MongoDB 服務:net start MongoDB

停止 MongoDB 服務:net stop MongoDB

刪除 MongoDB 服務:sc delete MongoDB

接下來就可以在 cmd 中運行 E:\MongoDB\Server\3.4\bin 里面的 *.exe 程序了

  • shell 控制臺 mongo
  • 數據庫的還原 mongorestore
  • 備份 mongodump
  1. mongodb 啟動的參數

mongoDB - 主要特點

  • MongoDB安裝簡單。
  • MongoDB的提供了一個面向文檔存儲,沒有表結構的概念,每天記錄可以有完全不同的結構,操作起來比較簡單和容易。
  • 完全的索引支持(單鍵索引、數組索引、全文索引、地理位置索引 等)
  • 你可以通過本地或者網絡創建數據鏡像,這使得MongoDB有更強的擴展性。
  • 如果負載的增加(需要更多的存儲空間和更強的處理能力) ,它可以分布在計算機網絡中的其他節點上這就是所謂的分片。
  • Mongo支持豐富的查詢表達式。查詢指令使用JSON形式的標記,可輕易查詢文檔中內嵌的對象及數組。
  • MongoDb 使用update()命令可以實現替換完成的文檔(數據)或者一些指定的數據字段 。
  • Mongodb中的Map/reduce主要是用來對數據進行批量處理和聚合操作。
  • Map和Reduce。Map函數調用emit(key,value)遍歷集合中所有的記錄,將key與value傳給Reduce函數進行處理。
  • Map函數和Reduce函數是使用Javascript編寫的,并可以通過db.runCommand或mapreduce命令來執行MapReduce操作。
  • GridFS是MongoDB中的一個內置功能,可以用于存放大量小文件。
  • MongoDB允許在服務端執行腳本,可以用Javascript編寫某個函數,直接在服務端執行,也可以把函數的定義存儲在服務端,下次直接調用即可。
  • MongoDB 支持多種編程語言:C C++ C# .NET Erlang Haskell Java JavaScript Lisp node.JS Perl PHP Python Ruby Scala 等

mongoDB - 工具

監控

  • Munin:網絡和系統監控工具
  • Gangila:網絡和系統監控工具
  • Cacti:用于查看CPU負載, 網絡帶寬利用率,它也提供了一個應用于監控 MongoDB 的插件。

GUI

  • Robomongo(Robo 3T)
  • Fang of Mongo – 網頁式,由Django和jQuery所構成。
  • Futon4Mongo – 一個CouchDB Futon web的mongodb山寨版。
  • Mongo3 – Ruby寫成。
  • MongoHub – 適用于OSX的應用程序。
  • Opricot – 一個基于瀏覽器的MongoDB控制臺, 由PHP撰寫而成。
  • Database Master — Windows的mongodb管理工具
  • RockMongo — 最好的PHP語言的MongoDB管理工具,輕量級, 支持多國語言.

mongoDB - 三大重要概念

1. database 數據庫

多個集合邏輯上組織在一起,就是數據庫。

數據庫命名規范:

  • 不能是空字符串("")。
  • 不得含有' '(空格)、.、$、/、\和\0 (空字符)。
  • 應全部小寫。
  • 最多64字節。

有一些數據庫名是保留的,可以直接訪問這些有特殊作用的數據庫。

  • admin: 從權限的角度來看,這是"root"數據庫。要是將一個用戶添加到這個數據庫,這個用戶自動繼承所有數據庫的權限。一些特定的服務器端命令也只能從這個數據庫運行,比如列出所有的數據庫或者關閉服務器。
  • local: 這個數據永遠不會被復制,可以用來存儲限于本地單臺服務器的任意集合
  • config: 當Mongo用于分片設置時,config數據庫在內部使用,用于保存分片的相關信息。

2. collection 集合

多個文檔組成一個集合,相當于關系數據庫的表。

所有存儲在集合中的數據都是 BSON 格式,BSON 是類 JSON 的一種二進制形式的存儲格式,簡稱 Binary JSON。

集合名命名規范:

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),這個字符表示集合名的結尾。
  • 集合名不能以"system."開頭,這是為系統集合保留的前綴。
  • 用戶創建的集合名字不能含有保留字符。有些驅動程序的確支持在集合名里面包含,這是因為某些系統生成的集合中包含該字符。除非你要訪問這種系統創建的集合,否則千萬不要在名字里出現$。

3. document 文檔

MongoDB 將數據存儲為一個文檔,數據結構由鍵值對組成。

MongoDB 文檔是一組鍵值對(即BSON,二進制的 JSON),類似于 JSON 對象。字段值可以包含其他文檔,數組及文檔數組。

文檔鍵命名規范:

  • 鍵不能含有\0 (空字符)。這個字符用來表示鍵的結尾。
  • .和$有特別的意義,只有在特定環境下才能使用。
  • 以下劃線"_"開頭的鍵是保留的(不是嚴格要求的)。

需要注意的是:

  • 文檔中的鍵值對是有序的。
  • 文檔中的值不僅可以是在雙引號里面的字符串,還可以是其他幾種數據類型(甚至可以是整個嵌入的文檔)。
  • MongoDB區分類型和大小寫。
  • MongoDB的文檔不能有重復的鍵。
  • 文檔的鍵是字符串。除了少數例外情況,鍵可以使用任意UTF-8字符。
image
image

MongoDB - 數據類型

ObjectId:主鍵,一種特殊而且非常重要的類型,每個文檔都會默認配置這個屬性,屬性名為_id,除非自己定義,方可覆蓋

MongoDB - 常見操作

查看當前數據庫

db

查看所有數據庫

沒有數據的數據庫不予顯示

MongoDB 中默認的數據庫為 test,如果你沒有創建新的數據庫,集合將存放在 test 數據庫中。

show dbs

連接到指定的數據庫

如果數據庫不存在,則創建數據庫,否則切換到指定數據庫。

use db_name

查看服務器狀態

db.serverStatus()

查看數據庫統計信息

db.stats()

刪除數據庫

db.dropDatabase()

查看數據庫中所有集合

show tables
或
show collections

清空集合

刪除里面的文檔,但集合還在

db.col_name.remove({})

刪除集合

db.col_name.drop()

查看集合詳細信息

MongoDB 的3.0后的版本分了三種模式 queryPlanner、executionStats、allPlansExecution

db.col_name.find({key:value}).explain("allPlansExecution")

MongoDB - 增刪改查

插入

MongoDB 使用 insert() 或 save() 方法向集合中插入文檔:

如果該集合不在該數據庫中, MongoDB 會自動創建該集合并插入文檔。

insert() 或 save() 方法都可以向collection里插入數據,兩者區別:

  • 如果不指定 _id 字段,save() 方法類似于 insert() 方法。如果指定 _id 字段,則會更新該 _id 的數據。
  • 使用save函數,如果原來的對象不存在,那他們都可以向collection里插入數據,如果已經存在,save會調用update更新里面的記錄,而insert則會忽略操作
  • insert可以一次性插入一個列表,而不用遍歷,效率高, save則需要遍歷列表,一個個插入。
db.col_name.insert(document)

db.col_name.save(document)

插入一個文檔到 col 集合中:

db.col_1.insert({
    title: 'MongoDB 教程',
    description: 'MongoDB 是一個 Nosql 數據庫',
    by: '菜鳥教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})

也可以將文檔數據定義為一個變量,如下所示:

document = ({
    title: 'MongoDB 教程',
    description: 'MongoDB 是一個 Nosql 數據庫',
    by: '菜鳥教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
});

db.col_2.insert(document)

刪除

remove() 函數是用來刪除集合中的數據

在執行 remove() 函數前先執行 find() 命令來判斷執行的條件是否正確,這是一個比較好的習慣。

db.col_name.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)

- query :(可選)刪除的文檔的條件。
- justOne : (可選)如果設為 true 或 1,則只刪除一個文檔。
- writeConcern :(可選)拋出異常的級別。

刪除集合中所有文檔

db.col.remove({})

移除 col_1 集合中 title 為 MongoDB save 的文檔,只刪除第一條找到的記錄

db.col_1.remove({'title':'MongoDB save'}, 1)

更新

MongoDB 使用 update() 和 save() 方法來更新集合中的文檔

update() 方法用于更新已存在的文檔

db.col_name.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

- query : update 的查詢條件,類似sql update查詢內where后面的。
- update : update的對象和一些更新的操作符(如$,$inc...)等,也可以理解為sql update查詢內set后面的
- upsert : 可選,這個參數的意思是,如果不存在 update 的記錄,是否插入記錄,true 為插入,默認是 false,不插入。
- multi : 可選,mongodb 默認是false,只更新找到的第一條記錄,如果這個參數為true,就把按條件查出來多條記錄全部更新。
- writeConcern :可選,拋出異常的級別。

通過 update() 方法來更新 col_1 集合中的 title

$set 操作符為部分更新操作符,只更新 $set 之后的數據,而不是覆蓋之前的數據

db.col_1.update({ 'title': 'MongoDB 教程' }, { $set: { 'title': 'MongoDB' } })

以上語句只會修改第一條發現的文檔,如果要修改多條相同的文檔,則需要設置 multi 參數為 true。

db.col_1.update({ 'title': 'MongoDB 教程' }, { $set: { 'title': 'MongoDB' } }, { multi: true })

save() 方法通過傳入的文檔來替換已有文檔。語法格式如下:

db.col_name.save(
   <document>,
   {
     writeConcern: <document>
   }
)

以下實例中我們替換了 col_1 的文檔數據:

document = ({
    "_id": "1",
    "title": "MongoDB save",
    "description": "MongoDB 是一個 Nosql 數據庫",
    "by": "菜鳥",
    "url": "http://www.runoob.com",
    "tags": ["mongodb", "database", "NoSQL"],
});

db.col_1.save(document)

查詢

find() 方法,它返回集合中所有文檔。

findOne() 方法,它只返回一個文檔。

db.col_name.find(query, projection)

- query :可選,使用查詢操作符指定查詢條件
- projection :可選,使用投影操作符指定返回的鍵。查詢時返回文檔中所有鍵值, 只需省略該參數即可(默認省略)。

格式化輸出:

db.col_name.find().pretty()

查看集合中文檔的個數:

db.col_name.find().count()

跳過指定數量的數據:

db.col_name.find().skip()

讀取指定記錄的條數:

db.col_name.find().limit()

排序:

sort()方法可以通過參數指定排序的字段,并使用 1 和 -1 來指定排序的方式,其中 1 為升序排列,而-1是用于降序排列。

db.col_name.find().sort({key:1})

sort()方法可以通過參數指定排序的字段,并使用 1 和 -1 來指定排序的方式,其中 1 為升序排列,而-1是用于降序排列。

Where 語句

如果你想獲取"col"集合中 "likes" 大于100,小于 200 的數據,你可以使用以下命令:

db.col.find({likes : {$lt :200, $gt : 100}})

// 類似于SQL語句:
Select * from col where likes>100 AND  likes<200;
條件操作符 中文 全英文
$gt 大于 greater than
$gte 大于等于 greater than equal
$lt 小于 less than
$lte 小于等于 less than equal
$ne 不等于 not equal

$type 操作符

用來檢索集合中匹配的數據類型

如果想獲取 "col" 集合中 title 為 String 的數據,你可以使用以下命令:

db.col.find({"title" : {$type : 2}})

AND 條件

find() 方法可以傳入多個鍵(key),每個鍵(key)以逗號隔開,語法格式如下:

db.col_name.find({key1:value1, key2:value2}).pretty()

// 類似于 SQL and 語句:
SELECT * FROM col_name WHERE key1='value1' AND key2=value2

OR 條件

db.col_name.find({ $or: [{ "by": "菜鳥教程" }, { "title": "MongoDB 教程" }] }).pretty()

// 類似于 SQL or 語句:
SELECT * FROM col_name WHERE key1=value1 OR key2=value2

AND 和 OR 聯合使用

db.col_name.find({
    "likes": {
        $gt: 50
    },
    $or: [{
        "by": "菜鳥教程"
    }, {
        "title": "MongoDB 教程"
    }]
}).pretty()

// 類似常規 SQL 語句:
SELECT * FROM col_name where likes>50 AND (by = '菜鳥教程' OR title = 'MongoDB 教程')

MongoDB - 索引

注意:從 mongoDB 3.0 開始,ensureIndex 被廢棄,今后都僅僅是 createIndex 的一個別名。

索引通常能夠極大的==提高查詢的效率==,如果沒有索引,MongoDB在讀取數據時必須掃描集合中的每個文件并選取那些符合查詢條件的記錄。

這種掃描全集合的查詢效率是非常低的,特別在處理大量的數據時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的性能是非常致命的。

索引是特殊的數據結構,索引存儲在一個易于遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構

索引常用命令

getIndexes 查看集合索引情況

db.col_name.getIndexes()

hint 強制使用索引

db.col_name.find({age:{$lt:30}}).hint({name:1, age:1}).explain()

刪除索引(不會刪除 _id 索引)

db.col_name.dropIndexes()

db.col_name.dropIndex({firstname: 1})

createIndex() 方法

MongoDB使用 createIndex() 方法來創建索引

key 為你要創建的索引字段,1為按升序創建索引,-1為按降序創建索引。

也可以設置使用多個字段創建索引(關系型數據庫中稱作復合索引)

db.col_name.createIndex({key:1})

createIndex() 接收可選參數,可選參數列表如下:

_id 索引

對于每個插入的數據,都會自動生成一條唯一的 _id 字段,_id 索引是絕大多數集合默認建立的索引

> db.col_1.insert({x:10})
WriteResult({ "nInserted" : 1 })

> db.col_1.find()
{ "_id" : ObjectId("59658e56aaf42d1c98dd95a2"), "x" : 10 }

> db.col_1.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "runoob.col_1"
        }
]

字段解釋:

  • v 表示 version,在 Mongo3.2 之前的版本中,會存在 {v:0}(版本鎖為0)的情況。在3.2之后的版本中,{v:0} 不再允許使用,這部分可以不去關注,因為 v 由系統自動管理

  • key 表示作為索引的鍵。1 或 -1表示排序模式,1為升序,1為降序

  • name 表示索引的名字,默認生成名稱的規則是作為索引的字段_排序模式

  • ns 表示 namespace 命名空間,由數據庫名稱.集合名稱組成

單鍵索引

最普通的索引,不會自動創建

// 對 x 字段創建升序索引

> db.col_1.createIndex({x:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

> db.col_1.find()
{ "_id" : ObjectId("59658e56aaf42d1c98dd95a2"), "x" : 10 }

> db.col_1.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "runoob.col_1"
        },
        {
                "v" : 2,
                "key" : {
                        "x" : 1
                },
                "name" : "x_1",
                "ns" : "runoob.col_1"
        }
]

多鍵索引

單鍵索引的值為一個單一的值,多鍵索引的值有多個數據(如數組)

如果mongoDB中插入數組類型的多鍵數據,索引是自動建立的,無需刻意指定

> db.col_1.insert({z:[1,2,3,4,5]})
WriteResult({ "nInserted" : 1 })

> db.col_1.find()
{ "_id" : ObjectId("59658e56aaf42d1c98dd95a2"), "x" : 10 }
{ "_id" : ObjectId("5965923eaaf42d1c98dd95a3"), "y" : 20 }
{ "_id" : ObjectId("59659828aaf42d1c98dd95a4"), "z" : [ 1, 2, 3, 4, 5 ] }

> db.col_1.find({z:3})
{ "_id" : ObjectId("59659828aaf42d1c98dd95a4"), "z" : [ 1, 2, 3, 4, 5 ] }

復合索引

同時對多個字段創建索引

> db.col_2.insert({x:10,y:20,z:30})
WriteResult({ "nInserted" : 1 })

> db.col_2.find()
{ "_id" : ObjectId("59659a57aaf42d1c98dd95a5"), "x" : 10, "y" : 20, "z" : 30 }

> db.col_2.createIndex({x:1,y:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

> db.col_2.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "runoob.col_2"
        },
        {
                "v" : 2,
                "key" : {
                        "x" : 1,
                        "y" : 1
                },
                "name" : "x_1_y_1",
                "ns" : "runoob.col_2"
        }
]

過期索引

又稱 TTL(Time To Live,生存時間)索引,即在一段時間后會過期的索引(如登錄信息、日志等)

過期后的索引會連同文檔一起刪除

expireAfterSeconds:指定一個以秒為單位的數值,設定集合的生存時間。

注意:

  • 存儲在過期索引字段的值必須是指定的時間類型(必須是 ISODate 或 ISODate 數組,不能使用時間戳,否則不能被自動刪除)
  • 如果指定了 ISODate 數組,則按照最小的時間進行刪除
  • 過期索引不能是復合索引(不能指定兩個過期時間)
  • 刪除時間存在些許誤差(1 分鐘左右)
> db.col_3.insert({x:new Date()})
WriteResult({ "nInserted" : 1 })

> db.col_3.find()
{ "_id" : ObjectId("59659f3baaf42d1c98dd95a7"), "x" : ISODate("2017-07-12T04:02:03.835Z") }

> db.col_3.createIndex({x:1},{expireAfterSeconds:10})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

> db.col_3.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "runoob.col_3"
        },
        {
                "v" : 2,
                "key" : {
                        "x" : 1
                },
                "name" : "x_1",
                "ns" : "runoob.col_3",
                "expireAfterSeconds" : 10
        }
]

> db.col_3.find()
// 無返回

全文索引

場景:全網站關鍵詞搜索

key-value 中,key 此時為 $**(也可以是具體某 key),value 此時為一個固定的字符串(如 text

全文索引相似度,與 sort 函數一起使用效果更好

db.col_7.find({ $text: { $search: "aa bb" } }, { score: { $meta: "textScore" } }).sort({ score: { $meta: "textScore" } })

注意:

  • 每個集合只能創建一個全文索引
  • MongoDB 從 2.4 版本開始支持全文檢索,從 3.2 版本開始支持中文
  • (好像)只能對整個單詞查詢,不能對單詞的截取部分查詢
  • 關鍵詞之間的空格表示
  • 關鍵詞之前的 - 表示
  • 關鍵詞加引號表示 (需用 \ 轉義)
> db.col_7.find()
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aa8faaf42d1c98dd95b1"), "title" : "abc def", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aedfaaf42d1c98dd95b2"), "title" : "aa bb", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }

> db.col_7.createIndex({"title": "text"})

> db.col_7.find({$text:{$search:"aa"}})
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aedfaaf42d1c98dd95b2"), "title" : "aa bb", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }

> db.col_7.find({$text:{$search:"aa cc"}})
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aedfaaf42d1c98dd95b2"), "title" : "aa bb", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }

> db.col_7.find({$text:{$search:"\"aa\" \"cc\""}})
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }

> db.col_7.find({$text:{$search:"aa bb"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}})
{ "_id" : ObjectId("5965aedfaaf42d1c98dd95b2"), "title" : "aa bb", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》", "score" : 1.5 }
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》", "score" : 1.3333333333333333 }


> db.col_7.dropIndexes()

> db.col_7.createIndex({"author": "text"}))

> db.col_7.find({$text:{$search:"小明"}})})
>

> db.col_7.find({$text:{$search:"白小明"}})
{ "_id" : ObjectId("5965aa84aaf42d1c98dd95b0"), "title" : "aa bb cc", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aa8faaf42d1c98dd95b1"), "title" : "abc def", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }
{ "_id" : ObjectId("5965aedfaaf42d1c98dd95b2"), "title" : "aa bb", "author" : "白小明", "article" : "這是白小明的一篇文章,標題《aa bb cc》" }

地理位置索引

查看最近的點

MongoDB - 聚合

==分組計算==

MongoDB 中聚合主要用于處理數據(如平均值,求和等),并返回計算后的數據結果。類似sql語句中的 count(*)。

aggregate() 方法

db.col_name.aggregate(AGGREGATE_OPERATION)

下表展示了一些聚合的表達式:

實例

計算每個作者所寫的文章數

在下面的例子中,我們通過字段by_user字段對數據進行分組,并計算by_user字段相同值的總和。

集合中的數據如下:

{
        "_id" : ObjectId("5963b992a812aa05b9d2e765"),
        "title" : "MongoDB Overview",
        "description" : "MongoDB is no sql database",
        "by_user" : "runoob.com",
        "url" : "http://www.runoob.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
{
        "_id" : ObjectId("5963b9aaa812aa05b9d2e766"),
        "title" : "NoSQL Overview",
        "description" : "No sql database is very fast",
        "by_user" : "runoob.com",
        "url" : "http://www.runoob.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 10
}
{
        "_id" : ObjectId("5963b9bba812aa05b9d2e767"),
        "title" : "Neo4j Overview",
        "description" : "Neo4j is no sql database",
        "by_user" : "Neo4j",
        "url" : "http://www.neo4j.com",
        "tags" : [
                "neo4j",
                "database",
                "NoSQL"
        ],
        "likes" : 750
}

使用aggregate()計算結果如下:

db.col_1.aggregate([{
    $group: {
        _id: "$by_user",
        num_tutorial: {
            $sum: 1
        }
    }
}])

// 返回
{ "_id" : "Neo4j", "num_tutorial" : 1 }
{ "_id" : "runoob.com", "num_tutorial" : 2 }

// 以上實例類似sql語句
select by_user, count(*) from col_1 group by by_user

聚合管道

管道在Unix和Linux中一般用于將當前命令的輸出結果作為下一個命令的參數。

MongoDB 的聚合管道將MongoDB文檔在一個管道處理完畢后將結果傳遞給下一個管道處理。管道操作是可以重復的。

表達式:處理輸入文檔并輸出。表達式是無狀態的,只能用于計算當前聚合管道的文檔,不能處理其它的文檔。

聚合管道常用的幾個操作:

  • $project:修改輸入文檔的結構。可以用來重命名、增加或刪除域,也可以用于創建計算結果以及嵌套文檔。
  • $match:用于過濾數據,只輸出符合條件的文檔。$match使用MongoDB的標準查詢操作。
  • $limit:用來限制MongoDB聚合管道返回的文檔數。
  • $skip:在聚合管道中跳過指定數量的文檔,并返回余下的文檔。
  • $unwind:將文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值。
  • $group:將集合中的文檔分組,可用于統計結果。
  • $sort:將輸入文檔排序后輸出。
  • $geoNear:輸出接近某一地理位置的有序文檔。

實例

$project 實例

0 為不顯示,1為顯示,默認情況下 _id 字段是 1

db.articles.aggregate({
    $project: {
        _id: 0,
        title: 1,
        by_user: 1,
    }
});

 // 返回
{ "title" : "MongoDB Overview", "by_user" : "runoob.com" }
{ "title" : "NoSQL Overview", "by_user" : "runoob.com" }
{ "title" : "Neo4j Overview", "by_user" : "Neo4j" }

$match 實例

$match 用于獲取分數大于70小于或等于90記錄,然后將符合條件的記錄送到下一階段$group管道操作符進行處理。

db.articles.aggregate([
    { $match: { score: { $gt: 70, $lte: 90 } } },
    { $group: { _id: null, count: { $sum: 1 } } }
]);

 // 返回
{ "_id" : null, "count" : 1 }

$skip 實例

經過 $skip 管道操作符處理后,前2個文檔被"過濾"掉。

db.col_1.aggregate({ $skip: 2 });

MongoDB - 復制

MongoDB 復制(副本集)是==將數據同步在多個服務器==的過程。

復制提供了數據的冗余備份,并在多個服務器上存儲數據副本,提高了數據的可用性, 并可以保證數據的安全性。

特點:

  • 保障數據的安全性

  • 數據高可用性 (24*7)

  • 災難恢復,復制允許您從硬件故障和服務中斷中恢復數據。

  • 無需停機維護(如備份,重建索引,壓縮)

  • 分布式讀取數據

  • N 個節點的集群

  • 任何節點可作為主節點

  • 所有寫入操作都在主節點上

  • 自動故障轉移

  • 自動恢復

復制原理

mongodb 的復制至少需要兩個節點。

  • 其中一個是==主節點==,負責處理客戶端請求,
  • 其余的都是==從節點==,負責復制主節點上的數據。

mongodb各個節點常見的搭配方式為:一主一從、一主多從。

主節點記錄在其上的所有操作oplog,從節點定期輪詢主節點獲取這些操作,然后對自己的數據副本執行這些操作,從而保證從節點的數據與主節點一致。

image

復制設置

  1. 關閉正在運行的MongoDB服務器。

現在我們通過指定 --replSet 選項來啟動mongoDB

mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"

實例:

下面實例會啟動一個名為rs0的MongoDB實例,其端口號為27017。

啟動后打開命令提示框并連接上mongoDB服務。

在Mongo客戶端使用命令rs.initiate()來啟動一個新的副本集。

我們可以使用rs.conf()來查看副本集的配置

查看副本集狀態使用 rs.status() 命令

mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0

副本集添加成員

添加副本集的成員,我們需要使用多條服務器來啟動mongo服務。

進入Mongo客戶端,并使用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存儲海量的數據時,==一臺機器可能不足以存儲數據==,也可能不足以提供可接受的讀寫吞吐量。這時,我們就可以通過在多臺機器上分割數據,使得數據庫系統能存儲和處理更多的數據。

為什么使用分片?

  • 復制所有的寫入操作到主節點
  • 延遲的敏感數據會在主節點查詢
  • 單個副本集限制在12個節點
  • 當請求量巨大時會出現內存不足。
  • 本地磁盤不足
  • 垂直擴展價格昂貴

分片集群結構

image

三個主要組件:

  • Shard: 用于存儲實際的數據塊,實際生產環境中一個shard server角色可由幾臺機器組個一個replica set承擔,防止主機單點故障
  • Config Server: mongod實例,存儲了整個 ClusterMetadata,其中包括 chunk信息。
  • Query Routers: 前端路由,客戶端由此接入,且讓整個集群看上去像單一數據庫,前端應用可以透明使用。

MongoDB - 監控

監控可以了解 MongoDB 的==運行情況==及==性能==

MongoDB中提供了 mongostat 和 mongotop 兩個命令來監控MongoDB的運行情況。

mongostat

它會間隔固定時間獲取 mongodb 的當前運行狀態,并輸出。

如果你發現數據庫突然變慢或者有其他問題的話,你第一手的操作就考慮采用 mongostat 來查看 mongo 的狀態。

mongostat

mongotop

mongotop用來跟蹤MongoDB的實例,提供每個集合的統計數據。默認情況下,mongotop每一秒刷新一次。

mongotop

輸出結果字段說明:

  • ns:包含數據庫命名空間,后者結合了數據庫名稱和集合。
  • db:包含數據庫的名稱。名為 . 的數據庫針對全局鎖定,而非特定數據庫。
  • total:mongod花費的時間工作在這個命名空間提供總額。
  • read:提供了大量的時間,這mongod花費在執行讀操作,在此命名空間。
  • write:提供這個命名空間進行寫操作,這mongod花了大量的時間。

等待的時間長度,以秒為單位,默認 1s

mongotop 10

報告每個數據庫的的使用

mongotop --locks

MongoDB - 備份與恢復

mongodump

在Mongodb中我們使用 mongodump 命令來備份MongoDB數據。

該命令可以導出所有數據到指定目錄中。

mongodump命令可以通過參數指定導出的數據量級轉存的服務器。

mongodump -h dbhost -d dbname -o dbdirectory

-h:MongDB所在服務器地址,例如:127.0.0.1,當然也可以指定端口號:127.0.0.1:27017
-d:需要備份的數據庫實例,例如:test
-o:備份的數據存放位置,例如:c:\data\dump,當然該目錄需要提前建立,在備份完成后,系統自動在dump目錄下建立一個test目錄,這個目錄里面存放該數據庫實例的備份數據。

實例

備份 mongodb_study 數據庫中的所有集合到 E:\MongoDB\dump

mongodump -h 127.0.0.1 -d mongodb_study -o E:\MongoDB\dump

不帶任何參數,即在當前目錄下備份所有數據庫實例

mongodump

備份所有MongoDB數據

mongodump --host HOST_NAME --port PORT_NUMBER

// 如
mongodump --host w3cschool.cc --port 27017

備份指定數據庫的集合

mongodump --collection COLLECTION_NAME --db DB_NAME

// 如
mongodump --collection mycol --db test

mongorestore

在Mongodb中我們使用 mongorestore 命令來恢復MongoDB數據。

mongorestore -h <hostname><:port> -d dbname <path>

--host <:port>, -h <:port>:MongoDB所在服務器地址,默認為: localhost:27017
--db , -d :需要恢復的數據庫實例,例如:test,當然這個名稱也可以和備份時候的不一樣,比如test2
--drop:恢復的時候,先刪除當前數據,然后恢復備份的數據。就是說,恢復后,備份后添加修改的數據都會被刪除,慎用哦!
<path>:mongorestore 最后的一個參數,設置備份數據所在位置,例如:c:\data\dump\test。你不能同時指定 <path> 和 --dir 選項,--dir也可以設置備份目錄。
--dir:指定備份的目錄,你不能同時指定 <path> 和 --dir 選項。

實例

恢復存放在 E:\MongoDB\dump 中的數據庫 mongodb_study,恢復前后的數據庫名不必相同

mongorestore -h localhost /db mongodb_study /dir E:\MongoDB\dump\mongodb_study

Node.js 連接 MongoDB

與 MySQL 不同的是 MongoDB 會自動創建數據庫和集合,所以使用前我們不需要手動去創建。

安裝驅動:npm install mongodb

運行 node:node connect

實例

connect.js

const MongoClient = require('mongodb').MongoClient;

// 自動創建數據庫 runoob
let mongoConnect = 'mongodb://localhost:27017/runoob';

// 插入數據,插入到數據庫 runoob 的 site 集合中
let insertData = function(db, callback) {
    // 自動創建集合 site
    let collection = db.collection('site');
    // 插入文檔
    let data = [{
        "name": "菜鳥教程",
        "url": "www.runoob.com"
    }, {
        "name": "菜鳥工具",
        "url": "c.runoob.com"
    }];

    collection.insert(data, function(err, result) {
        if (err) {
            console.log('Error:' + err);
            return;
        }
        callback(result);
    });
};

// 刪除數據,刪除所有 name 為 "菜鳥工具" 的文檔
let deleteData = function(db, callback) {
    let collection = db.collection('site');
    let whereStr = {
        "name": "菜鳥工具"
    };

    collection.remove(whereStr, function(err, result) {
        if (err) {
            console.log('Error:' + err);
            return;
        }
        callback(result);
    });
};

// 修改數據,將所以 name 為 "菜鳥教程" 的 url 改為 https://www.runoob.com
let updateData = function(db, callback) {
    let collection = db.collection('site');
    let whereStr = {
        "name": "菜鳥教程"
    };
    let updateStr = {
        $set: {
            "url": "https://www.runoob.com"
        }
    };

    collection.update(whereStr, updateStr, {
        multi: true
    }, function(err, result) {
        if (err) {
            console.log('Error:' + err);
            return;
        }
        callback(result);
    });
};

// 查詢數據,查詢 name 為 "菜鳥教程" 的數據
let selectData = function(db, callback) {
    let collection = db.collection('site');
    let whereStr = {
        "name": '菜鳥教程'
    };

    collection.find(whereStr).toArray(function(err, result) {
        if (err) {
            console.log('Error:' + err);
            return;
        }
        callback(result);
    });
};

MongoClient.connect(mongoConnect, function(err, db) {
    console.log("連接成功!");

    insertData(db, function(result) {
        console.log("插入數據成功!");
        console.log(result);
        db.close();
    });

    deleteData(db, function(result) {
        console.log("刪除數據成功!");
        console.log(result);
        db.close();
    });

    updateData(db, function(result) {
        console.log("修改數據成功!");
        console.log(result);
        db.close();
    });

    selectData(db, function(result) {
        console.log("查詢數據成功!");
        console.log(result);
        db.close();
    });
});

mongoose

Mongoose學習參考文檔——基礎篇:https://cnodejs.org/topic/504b4924e2b84515770103dd

mongoose學習筆記:https://cnodejs.org/topic/58b911997872ea0864fee313

mongoose學習文檔:http://www.cnblogs.com/y-yxh/p/5689555.html

Nodejs學習筆記(十四)— Mongoose介紹和入門:http://www.cnblogs.com/zhongweiv/p/mongoose.html

Mongoose全面理解:http://www.cnblogs.com/jayruan/p/5123754.html

Node.js 有針對 MongoDB 的數據庫驅動:mongodb。你可以使用 npm install mongodb 來安裝。不過直接使用 mongodb 模塊雖然強大而靈活,但有些繁瑣,我就使用 mongoose 吧。

Mongoose 基于nodejs、構建在 mongodb 之上,使用 javascript 編程,是==連接 mongodb 數據庫的軟件包==,使mongodb的文檔數據模型變的優雅起來,方便對mongodb文檔型數據庫的連接和增刪改查等常規數據操作。

mongoose 是當前使用 mean(mongodb express angularjs nodejs)全棧開發必用的連接數據庫軟件包。

==mongoose ,提供了Schema、Model 和 Document 對象,用起來更為方便。== 另外,mongoose 還有 Query 和 Aggregate 對象:Query 實現查詢、Aggregate 實現聚合

mongoose 三個重要概念

Schema、Model、Entity 的關系:Schema生成Model,Model創造Entity,Model和Entity都可對數據庫操作造成影響,但Model比Entity更具操作性。

1. Schema 模式

Schema 對象定義==文檔結構==,可以定義字段、類型、唯一性、索引、驗證等。

Schema 不僅定義了文檔結構和使用性能,還可以有擴展插件、實例方法、靜態方法、復合索引、文檔生命周期鉤子

// new mongoose.Schema() 中傳入一個 JSON 對象,定義屬性和屬性類型
var BlogSchema = new mongoose.Schema({
    title: String,
    author: String
});

有的時候,我們創造的 Schema 不僅要為后面的 Model 和 Entity 提供公共的屬性,還要提供公共的方法。

2. Model 模型

Model 對象表示集合中的所有文檔

由 Schema 發布生成的模型,具有抽象屬性和行為的數據庫操作對

3. Document 文檔

Document 可等同于 Entity

由 Model 創建的實體,他的操作也會影響數據庫

使用

  1. 定義一個 Schema 模式
  2. 將該 Schema 發布為 Model
  3. 用 Model 創建 Entity
  4. Entity 是具有具體的數據庫操作 CRUD 的

mongoose 的 connection 對象定義了一些事件,比如 connected open close error 等,我們可以監聽這些事件。

const mongoose = require('mongoose');

let db = mongoose.connect('mongodb://127.0.0.1:27017/test');

db.connection.on('error', console.error.bind(console, '數據庫連接失敗:'));

db.connection.once('open', function() {
    console.log('數據庫連接成功!');

    // 定義一個 Schema 模式
    // new Schema() 中傳入一個 JSON 對象,定義屬性和屬性類型
    let PersonSchema = new mongoose.Schema({
        name: {
            type: String,
            unique: true
        },
        password: String
    });

    // 將該 Schema 發布為 Model
    let PersonModel = mongoose.model('col_1', PersonSchema);

    // 拿到了 Model 對象,就可以執行增刪改查等操作了
    // 如果要執行查詢,需要依賴 Model,當然 Entity 也是可以做到的
    PersonModel.find(function(err, result) {
        // 查詢到的所有person
    });

    // 用 Model 創建 Entity
    let personEntity = new PersonModel({
        name: 'Krouky',
        password: '10086'
    });

    // Entity 是具有具體的數據庫操作 CRUD 的
    // 執行完成后,數據庫就有該數據了
    personEntity.save(function(err, result) {
        if (err) {
            console.log(err);
        } else {
            console.log(`${result} saved!`);
        }
    });
});

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容