# MongoDB
## 數(shù)據(jù)庫(kù)分類(lèi)
### 關(guān)系型數(shù)據(jù)庫(kù)
* 具備ACID特性
? ? * Atomic原子性,也就是說(shuō)事務(wù)里的所有操作要么全部做完,要么都不做,事務(wù)成功的條件是事務(wù)里的所有操作都成功,只要有一個(gè)操作失敗,整個(gè)事務(wù)就失敗,需要回滾。*比如銀行轉(zhuǎn)賬,從A賬戶轉(zhuǎn)100元至B賬戶,分為兩個(gè)步驟:1)從A賬戶取100元;2)存入100元至B賬戶。這兩步要么一起完成,要么一起不完成,如果只完成第一步,第二步失敗,錢(qián)會(huì)莫名其妙少了100元。*
? ? * Consistency一致性,也就是說(shuō)數(shù)據(jù)庫(kù)要一直處于一致的狀態(tài),事務(wù)的運(yùn)行不會(huì)改變數(shù)據(jù)庫(kù)原本的一致性約束。*例如現(xiàn)有完整性約束a+b=10,如果一個(gè)事務(wù)改變了a,那么必須得改變b,使得事務(wù)結(jié)束后依然滿足a+b=10,否則事務(wù)*失敗。
? ? * Isolation獨(dú)立性,所謂的獨(dú)立性是指并發(fā)的事務(wù)之間不會(huì)互相影響,如果一個(gè)事務(wù)要訪問(wèn)的數(shù)據(jù)正在被另外一個(gè)事務(wù)修改,只要另外一個(gè)事務(wù)未提交,它所訪問(wèn)的數(shù)據(jù)就不受未提交事務(wù)的影響。*比如現(xiàn)在有個(gè)交易是從A賬戶轉(zhuǎn)100元至B賬戶,在這個(gè)交易還未完成的情況下,如果此時(shí)B查詢自己的賬戶,是看不到新增加的100元的。*
? ? * Durability持久性,持久性是指一旦事務(wù)提交后,它所做的修改將會(huì)永久的保存在數(shù)據(jù)庫(kù)上,即使出現(xiàn)宕機(jī)也不會(huì)丟失。
* 局限性和不適用場(chǎng)景
? ? * 關(guān)系型數(shù)據(jù)庫(kù)為了維護(hù)一致性所付出的巨大代價(jià)就是其讀寫(xiě)性能比較差。在網(wǎng)頁(yè)應(yīng)用中,尤其是SNS(社交)應(yīng)用中,一致性卻不是顯得那么重要,用戶A看到的內(nèi)容和用戶B看到同一用戶C內(nèi)容更新不一致是可以容忍的,或者說(shuō),兩個(gè)人看到同一好友的數(shù)據(jù)更新的時(shí)間差那么幾秒是可以容忍的,因此,關(guān)系型數(shù)據(jù)庫(kù)的最大特點(diǎn)在這里已經(jīng)無(wú)用武之地,起碼不是那么重要了。
? ? * 關(guān)系數(shù)據(jù)庫(kù)的另一個(gè)特點(diǎn)就是其具有固定的表結(jié)構(gòu),因此,其擴(kuò)展性比較差,而在SNS中,系統(tǒng)的升級(jí),功能的增加,往往意味著數(shù)據(jù)結(jié)構(gòu)巨大變動(dòng),這一點(diǎn)關(guān)系型數(shù)據(jù)庫(kù)也難以應(yīng)付,需要新的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)。
### **非關(guān)系型數(shù)據(jù)庫(kù) NoSQL(Not Only SQL )**
*? NoSQL數(shù)據(jù)庫(kù)的四大分類(lèi)
|分類(lèi) |舉例 |典型應(yīng)用場(chǎng)景 |數(shù)據(jù)模型 |優(yōu)點(diǎn) |缺點(diǎn) |
|--- |--- |--- |--- |--- |--- |
|鍵值(key-value)存儲(chǔ)數(shù)據(jù)庫(kù) |Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB |內(nèi)容緩存,主要用于處理大量數(shù)據(jù)的高訪問(wèn)負(fù)載,也用于一些日志系統(tǒng)等等。 |Key 指向 Value 的鍵值對(duì),通常用hash table來(lái)實(shí)現(xiàn) |查找速度快 |數(shù)據(jù)無(wú)結(jié)構(gòu)化,通常只被當(dāng)作字符串或者二進(jìn)制數(shù)據(jù) |
|--- |--- |--- |--- |--- |--- |
|**列存儲(chǔ)數(shù)據(jù)庫(kù)** |Cassandra, HBase, Riak |分布式的文件系統(tǒng) |以列簇式存儲(chǔ),將同一列數(shù)據(jù)存在一起 |查找速度快,可擴(kuò)展性強(qiáng),更容易進(jìn)行分布式擴(kuò)展 |功能相對(duì)局限 |
|**文檔型數(shù)據(jù)庫(kù)** |CouchDB, MongoDb |Web應(yīng)用(與Key-Value類(lèi)似,Value是結(jié)構(gòu)化的,不同的是數(shù)據(jù)庫(kù)能夠了解Value的內(nèi)容) |Key-Value對(duì)應(yīng)的鍵值對(duì),Value為結(jié)構(gòu)化數(shù)據(jù) |數(shù)據(jù)結(jié)構(gòu)要求不嚴(yán)格,表結(jié)構(gòu)可變,不需要像關(guān)系型數(shù)據(jù)庫(kù)一樣需要預(yù)先定義表結(jié)構(gòu) |查詢性能不高,而且缺乏統(tǒng)一的查詢語(yǔ)法。 |
|**圖形(Graph)數(shù)據(jù)庫(kù)** |Neo4J, InfoGrid, Infinite Graph |社交網(wǎng)絡(luò),推薦系統(tǒng)等。專注于構(gòu)建關(guān)系圖譜 |圖結(jié)構(gòu) |利用圖結(jié)構(gòu)相關(guān)算法。比如最短路徑尋址,N度關(guān)系查找等 |很多時(shí)候需要對(duì)整個(gè)圖做計(jì)算才能得出需要的信息,而且這種結(jié)構(gòu)不太好做分布式的集群方案。 |
* Mongodb
? ? * 是文檔型的非關(guān)系型數(shù)據(jù)庫(kù),使用bson( Binary Serialized Document Format) 結(jié)構(gòu)。其優(yōu)勢(shì)在于查詢功能比較強(qiáng)大,能存儲(chǔ)海量數(shù)據(jù)。
# Mac OSX 平臺(tái)安裝Mongo
## 使用 brew 下載并安裝
1、終端輸入:brew install mongodb
2、安裝成功后可以看到提示:
```
To have launchd start mongodb now and restart at login:
? brew services start mongodb
Or, if you don't want/need a background service you can just run:
? mongod --config /usr/local/etc/mongod.conf
==> Summary
??? /usr/local/Cellar/mongodb/4.0.0: 18 files, 268.4MB
```
## 運(yùn)行mongoDB服務(wù)
1、首先創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)存儲(chǔ)目錄 /data/db,命令行輸入:sudo mkdir -p /data/db
2、創(chuàng)建日志文件:sudo vim /data/db/mongo.log;創(chuàng)建pid文件:sudo vim /var/run/mongo.pid
3、啟動(dòng) mongodb,分兩種啟動(dòng)方式:
* 啟動(dòng)命令直接指定命令參數(shù)方式:sudo mongod -port 27017 -dbpath /data/db -fork? -pidfilepath=/var/run/mongo.pid? -logpath /data/db/mongo.log
* 啟動(dòng)命令指定配置文件方式:
? ? * 1、創(chuàng)建配置文件 :sudo vim /etc/mongodb.conf?

? ? *? ? 2、執(zhí)行:sudo mongo -f /etc/mongodb.conf
* 啟動(dòng)參數(shù)說(shuō)明(可以通過(guò)mongo -help來(lái)查看全部參數(shù)):
? ? * -port arg? ? #指定服務(wù)端口號(hào),默認(rèn)端口27017。
? ? * -dbpath? ? #? 指定存儲(chǔ)路徑。
? ? * -logpath arg? ? # 指定MongoDB日志文件,注意是指定文件不是目錄。使用fork參數(shù)時(shí)因?yàn)槿罩緹o(wú)法寫(xiě)到控制臺(tái),所以需要同時(shí)使用logpath參數(shù)。
? ? * -pidfilepath arg? ? # 指定PID File 的完整路徑,如果沒(méi)有設(shè)置,則沒(méi)有PID文件。
? ? * -fork? ? # 以守護(hù)進(jìn)程的方式運(yùn)行MongoDB,相當(dāng)于nohup “shell”? &用法。使用fork參數(shù)時(shí)因?yàn)槿罩緹o(wú)法寫(xiě)到控制臺(tái),所以需要同時(shí)使用logpath參數(shù)。
? ? * -directoryperdb? ? # 設(shè)置每個(gè)數(shù)據(jù)庫(kù)將被保存在一個(gè)單獨(dú)的目錄
? ? * -maxConns arg? # 最大同時(shí)連接數(shù) 默認(rèn)2000
? ? * -auth? ? #用戶認(rèn)證,默認(rèn)false,當(dāng)設(shè)置為true時(shí)候,進(jìn)入數(shù)據(jù)庫(kù)需要auth驗(yàn)證,當(dāng)數(shù)據(jù)庫(kù)里沒(méi)有用戶,則不需要驗(yàn)證也可以操作。直到創(chuàng)建了第一個(gè)用戶,之后操作都需要驗(yàn)證。
啟動(dòng)成功后可以看到以下提示:
```
[initandlisten] waiting for connections on port 27017
或
forked process: 41540
child process started successfully, parent exiting
```
## 停止mongoDB服務(wù)
正常關(guān)閉方式:
* 前臺(tái)方式啟動(dòng)時(shí),命令行光標(biāo)鍵入CTRL+C
* 通過(guò)連接的客戶端關(guān)閉,首先連接mongo成功后
? ? * 第一步:use admin
? ? * 第二步:db.shutdownServer()
異常關(guān)閉再次啟動(dòng)報(bào)錯(cuò)時(shí):
* 使用kill進(jìn)程方式
? ? * ps -ef |grep mongo
? ? * kill -15 pid? ? #建議不要使用 ”kill -9 pid“,因?yàn)槿绻\(yùn)行在沒(méi)開(kāi)啟日志(—journal)的情況下,可能會(huì)造成數(shù)據(jù)損失。
> 注意:在mongodb的啟動(dòng)時(shí),在數(shù)據(jù)目錄下,會(huì)生成一個(gè)mongod.lock文件。如果在正常退出時(shí),會(huì)清除這個(gè)mongod.lock文件,若是異常退出,在下次啟動(dòng)的時(shí)候,會(huì)禁止啟動(dòng),并看到下面的報(bào)錯(cuò)。
```
? ? exception in initAndListen: DBPathInUse: Unable to lock the lock file:
? ? /data/db/mongod.lock (Resource temporarily unavailable). Another mongod instance is already running on the /data/db directory, terminating
```
## Mongo連接
### 客戶端連接
語(yǔ)法格式:mongo 遠(yuǎn)程主機(jī)ip或DNS:端口號(hào)/數(shù)據(jù)庫(kù)名 -u user -p password
例:連接本地?cái)?shù)據(jù)庫(kù),在終端窗口輸入:mongo
```
~ ? mongo
MongoDB shell version v4.0.0
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 4.0.0
```
例:連接遠(yuǎn)程sit環(huán)境mongo數(shù)據(jù)庫(kù)
```
~ ? mongo 10.4.12.78/admin -u sit-user -p xxxxA5
MongoDB shell version v4.0.0
connecting to: mongodb://10.4.12.78:27017/admin
MongoDB server version: 3.2.12
```
### 標(biāo)準(zhǔn)uri連接
語(yǔ)法格式:mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
```
`#!/usr/bin/env python
``# -*- coding:utf-8 -*-
from pymongo import MongoClient
uri = 'mongodb://sit-user:xxxxqA5@10.4.12.78/admin'
con = MongoClient(uri)`
```
# MongoDB語(yǔ)法
## 概念術(shù)語(yǔ)
|SQL術(shù)語(yǔ)/概念 |MongoDB術(shù)語(yǔ)/概念 |解釋/說(shuō)明 |
|--- |--- |--- |
|database |database |數(shù)據(jù)庫(kù) |
|--- |--- |--- |
|table |collection |數(shù)據(jù)庫(kù)表/集合 |
|row |document |數(shù)據(jù)記錄行/文檔 |
|column |field |數(shù)據(jù)字段 |
|index |index |索引 |
|primary key |primary key |主鍵,MongoDB自動(dòng)將_id字段設(shè)置為主鍵 |

## 數(shù)據(jù)庫(kù)操作
### 創(chuàng)建數(shù)據(jù)庫(kù)
1、語(yǔ)法格式:use <database_name>? ? #如果數(shù)據(jù)庫(kù)存在時(shí)會(huì)切換到該數(shù)據(jù)庫(kù),不存在時(shí)會(huì)創(chuàng)建該數(shù)據(jù)庫(kù)。
2、操作成功后可以看到提示:
```
> use sms
switched to db sms
```
### 刪除數(shù)據(jù)庫(kù)
1、語(yǔ)法格式:db.dropDatabase()? ? #刪除當(dāng)前使用的數(shù)據(jù)庫(kù),可以通過(guò)命令“db”來(lái)查看當(dāng)前數(shù)據(jù)庫(kù)
2、操作成功后可以看到提示:
```
> db? ? #查看當(dāng)前數(shù)據(jù)庫(kù)
sms
> db.dropDatabase()
{ "ok" : 1 }
```
## 集合操作
### 創(chuàng)建集合
1、語(yǔ)法格式:db.createCollection(<name>,{ <options>})? ? #name為要?jiǎng)?chuàng)建的集合名,options為可選參數(shù):
* capped? ? #布爾類(lèi)型,如果為 true,則創(chuàng)建固定集合。固定集合是指有著固定大小的集合,當(dāng)達(dá)到最大值時(shí),它會(huì)自動(dòng)覆蓋最早的文檔。當(dāng)該值為 true 時(shí),必須指定 size 參數(shù)。
* size? ? #數(shù)值,為固定集合指定一個(gè)最大值(以字節(jié)計(jì))。如果 capped 為 true,也需要指定該字段**。**
* max? ? #數(shù)值,指定固定集合中包含文檔的最大數(shù)量。
2、執(zhí)行:db.createCollection("seller",{ capped : true, size :6142800, max : 10000 },創(chuàng)建集合成功后可以看到提示:
```
> db? ? #查看當(dāng)前數(shù)據(jù)庫(kù)
sms
> db.createCollection("seller",{ capped : true, size :6142800, max : 10000 })
{ "ok" : 1 }
```
### 刪除集合
1、語(yǔ)法格式:db.collection_name.drop()
2、刪除集合成功后,可以看到命令行返回“true”,否則返回“false”。
```
> show collections? ? #查看當(dāng)前數(shù)據(jù)庫(kù)下的集合
ark_open_api_account
seller
> db.ark_open_api_account.drop()
true
> show collections
seller
```
## 文檔操作
### 創(chuàng)建文檔
1、語(yǔ)法格式:db.collection_name.insert(<document>)
2、執(zhí)行db.seller.insert({"role" : "partner","shopname" : "redqa009-測(cè)試","email" : "[xx@163.com](mailto:xx@163.com)"})?
```
> db.seller.insert({"role" : "partner","shopname" : "redqa009-測(cè)試","email" : "[xx@163.com](mailto:xx@163.com)"})
WriteResult({ "nInserted" : 1 })
> db.seller.find()? ? #查詢文檔
{ "_id" : ObjectId("5b554bd275f30ccd39ca6483"), "role" : "partner", "shopname" : "redqa009-測(cè)試", "email" : "xx@163.com" }
```
或? 先定義document后再執(zhí)行insert(document)
```
> document=([{"role" : "partner","shopname" : "redqa007品牌店","email" : "1401261542@qq.com"},{"role" : "partner","shopname" : "redqa018","email" : "qatest6@redqa.xyz"}])
[
{
"role" : "partner",
"shopname" : "redqa007品牌店",
"email" : "1401261542@qq.com"
},
{
"role" : "partner",
"shopname" : "redqa018",
"email" : "[qatest6@redqa.xyz](mailto:qatest6@redqa.xyz)"
}
]
> db.seller.insert(document)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.seller.find()
{ "_id" : ObjectId("5b554bd275f30ccd39ca6483"), "role" : "partner", "shopname" : "redqa009-測(cè)試", "email" : "[xx@163.com](mailto:xx@163.com)" }
{ "_id" : ObjectId("5b554ee875f30ccd39ca6484"), "role" : "partner", "shopname" : "redqa007品牌店", "email" : "xx@qq.com" }
{ "_id" : ObjectId("5b554ee875f30ccd39ca6485"), "role" : "partner", "shopname" : "redqa018","email" : "xxx@redqa.xyz" }
```
### 更新文檔
* update()方法
? ? * 1、語(yǔ)法格式:
```
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,? # 可選,含義為如果不存在update的記錄,是否插入objNew,true為插入,默認(rèn)是false,不插入。
multi: <boolean>,? # 可選,默認(rèn)是false,只更新找到的第一條記錄,如果這個(gè)參數(shù)為true,就把按條件查出來(lái)多條記錄全部更新。
writeConcern: <document>? # writeConcern** **:可選,拋出異常的級(jí)別。
}
)
```
? ? * 2、更新shopname為"redqa009-測(cè)試1”商家的郵箱
? ? * 執(zhí)行'db.seller.update({shopname:"redqa009-測(cè)試1"},{$set:{email:"xx@163.com"}},{upsert:true})'
```
> db.seller.find()
{ "_id" : ObjectId("5b554bd275f30ccd39ca6483"), "role" : "partner", "shopname" : "redqa009-測(cè)試", "email" : "lxx@163.com" }
{ "_id" : ObjectId("5b554ee875f30ccd39ca6484"), "role" : "partner", "shopname" : "redqa007品牌店", "email" : "xx@qq.com" }
{ "_id" : ObjectId("5b554ee875f30ccd39ca6485"), "role" : "partner", "shopname" : "redqa018", "email" : "xx@redqa.xyz" }
> db.seller.update({shopname:"redqa009-測(cè)試1"},{$set:{email:"xx@163.com"}},{upsert:true})
WriteResult({
? ? "nMatched" : 0,
? ? "nUpserted" : 1,
? ? "nModified" : 0,
? ? "_id" : ObjectId("5b556dca30563a38f0284e00")
})
```
> 注意:當(dāng)集合為Capped collection時(shí),如果更新或替換操作更改了文檔大小,則操作將失敗。錯(cuò)誤提示如下:
```
WriteResult({
? ? "nMatched" : 0,
? ? "nUpserted" : 0,
? ? "nModified" : 0,
? ? "writeError" : {
? ? ? ? "code" : 10003,
? ? ? ? "errmsg" : "Cannot change the size of a document in a capped collection: 79 != 77"
? ? }
})
```
* save()方法
? ? * 1、語(yǔ)法格式
```
db.collection.save(
? <document>,? ? # 根據(jù)文檔中的'_id'字段,找到一個(gè)已經(jīng)存在的文檔,進(jìn)行更新。
? {
? ? writeConcern: <document>
? }
)
```
> 補(bǔ)充:
> 當(dāng)文檔中包含'_id'字段,但匹配不到已存在的文檔,也會(huì)將文檔插入數(shù)據(jù)庫(kù)。
> 當(dāng)文檔中不含'_id'字段,save方法將調(diào)用insert方法,插入這條文檔并分配一個(gè)_id。
### 刪除文檔
1、語(yǔ)法格式:
```
db.collection.remove(
? <query>,? ? # 可選,刪除的文檔的條件。
? {
? ? justOne: <boolean>,? ? # 可選,如果設(shè)為 true 或 1,則只刪除一個(gè)文檔。
? ? writeConcern: <document>
? }
)
```
> 注意:Capped collection不允許使用remove()方法刪除,只能使用db.collection.drop()方法刪除集合。db.collection.isCapped() 命令可以查看一個(gè)集合是否是 Capped Collection。
### 查詢文檔
```
1、db.collection.find(query, projection) # 可選,query為查詢條件,可選,projection指定返回的鍵。
```
### 比較操作符
* 大于:$gt
* 大于等于:$gte
* 小于:$lt
* 小于等于:$lte
* 等于:$eq
例:查詢訂單“total_discounted_price”大于等于100并且小于等于200的任意一個(gè)訂單號(hào)。
```
db.order.findOne({total_discounted_price:{$gte:150,$lte:200}},{order_id:1})
#projection:1代表只返回指定字段,為0代表不返回該字段。
```
* 不等于:$ne
例:查詢商家("seller":"53df5710b4c4d6383ae8e9a6")任意一個(gè)不是“已取消”的訂單
```
db.order_package.find({"seller":"53df5710b4c4d6383ae8e9a6","status":{$ne:998}})
```
* 匹配數(shù)組中任意值:$in
例:查詢所有訂單狀態(tài)狀態(tài)為“待配貨”、“配貨中”或“已發(fā)貨”的訂單。
```
db.order_package.find({ "status": {$in:[4,5,6]}})
```
* 不匹配數(shù)組中任意值:$nin
### 邏輯操作符
* 與查詢:$and
例:查詢商家("seller":"53df5710b4c4d6383ae8e9a6")所有訂單狀態(tài)狀態(tài)為“待配貨”、“配貨中”或“已發(fā)貨”的訂單。
```
db.order_package.find({$and:[{"seller":"53df5710b4c4d6383ae8e9a6"},{"status": {$in:[4,5,6]}}]})
等同于
db.order_package.find({ "seller":"53df5710b4c4d6383ae8e9a6","status": {$in:[4,5,6]}})
```
* 或查詢:$or
例:查詢訂單“total_discounted_price”小于等于100或大于等于200的訂單號(hào)。
```
db.order.find({$or:[{total_discounted_price:{$lte:100}},{total_discounted_price:{$gte:200}}]})
```
### 元素操作符
* 查詢是否存在某字段:$exists
例:查詢維護(hù)了貿(mào)易模式的所有商家。
```
db.seller.find({ "trade_mode":{$exists:true} })
```
*? 查詢數(shù)組中元素是否滿足指定的條件:$elemMatch
例:查詢所有支持“red_bonded”物流模式的商家。
```
被查詢集合的數(shù)據(jù)結(jié)構(gòu):
{
? "_id": ObjectId("5a151d9deb90b912e76ee832"),
? "shopname": "redqa009-測(cè)試",
? "logistics_infos": [
? ? {
? ? ? "name": "red_auto",
? ? ? "logistics": "auto",
? ? ? "is_default": true
? ? },
? ? {
? ? ? "name": "red_bonded",
? ? ? "logistics": "bonded",
? ? ? "customs_code": "SHANGHAI",
? ? ? "is_default": false
? ? }
? ],
? "trade_mode": 0
}
寫(xiě)法:
db.seller.find({ "logistics_infos":{$elemMatch:{name:"red_bonded"}} })
```
### 排序
* 升序
```
db.COLLECTION_NAME.find().sort({KEY:1})
```
* 降序
```
db.COLLECTION_NAME.find().sort({KEY:-1})
```
# MongoDB訪問(wèn)權(quán)限控制
## 訪問(wèn)控制參數(shù)
### **綁定IP地址**
* mongod 啟動(dòng)參數(shù):-bind_ip? <ip_address>?
默認(rèn)值是所有的IP地址都能訪問(wèn),該參數(shù)指定MongoDB對(duì)外提供服務(wù)的綁定IP地址,用于監(jiān)聽(tīng)客戶端 Application的連接,客戶端只能使用綁定的IP地址才能訪問(wèn)mongod,其他IP地址是無(wú)法訪問(wèn)的。
### **設(shè)置監(jiān)聽(tīng)端口**
* mongod 啟動(dòng)參數(shù):-port <port>? ?
MongoDB 默認(rèn)監(jiān)聽(tīng)的端口是27017,該參數(shù)顯式指定MongoDB實(shí)例監(jiān)聽(tīng)的TCP 端口,只有當(dāng)客戶端Application連接的端口和MongoDB實(shí)例監(jiān)聽(tīng)的端口一致時(shí),才能連接到MongoDB實(shí)例。
### **啟用用戶驗(yàn)證**
* mongod 啟動(dòng)參數(shù):-auth?
當(dāng)mongod 使用該參數(shù)啟動(dòng)時(shí),MongoDB會(huì)驗(yàn)證客戶端連接的賬戶和密碼,以確定其是否有訪問(wèn)的權(quán)限。如果認(rèn)證不通過(guò),那么客戶端不能訪問(wèn)MongoDB的數(shù)據(jù)庫(kù)。
### **權(quán)限認(rèn)證**
* mongo 連接參數(shù):-username <username>, -u <username>
* mongo 連接參數(shù):-password <password>, -p <password>
* mongo 連接參數(shù):-authenticationDatabase <dbname> 指定創(chuàng)建User的數(shù)據(jù)庫(kù);在特定的數(shù)據(jù)庫(kù)中創(chuàng)建User,該DB就是User的authentication database。
在連接mongo時(shí),使用參數(shù) --authenticationDatabase,會(huì)認(rèn)證 -u 和 -p 參數(shù)指定的賬戶和密碼。如果沒(méi)有指定驗(yàn)證數(shù)據(jù)庫(kù),mongo使用連接字符串中指定的DB作為驗(yàn)證數(shù)據(jù)塊。
```
mongo 10.4.12.78/admin -u sit-user -p xxxx4qA5 --authenticationDatabase "admin"
```
## **基于角色的訪問(wèn)控制**
**內(nèi)置角色**
內(nèi)置角色是MongoDB預(yù)定義的角色,操作的資源是在DB級(jí)別上。MongoDB擁有一個(gè)SuperUser的角色:root,擁有最大權(quán)限,能夠在系統(tǒng)的所有資源上執(zhí)行任意操作。
* 數(shù)據(jù)庫(kù)內(nèi)置用戶角色
? ? * read:授予User只讀數(shù)據(jù)的權(quán)限
? ? * readWrite:授予User讀寫(xiě)數(shù)據(jù)的權(quán)限
* 數(shù)據(jù)庫(kù)內(nèi)置管理角色
? ? * dbAdmin:在當(dāng)前dB中執(zhí)行管理操作
? ? * dbOwner:在當(dāng)前DB中執(zhí)行任意操作
? ? * userAdmin:在當(dāng)前DB中管理User
* 所有數(shù)據(jù)庫(kù)角色
? ? * readAnyDatabase
? ? * readWriteAnyDatabase
? ? * userAdminAnyDatabase
? ? * dbAdminAnyDatabase
* 超級(jí)用戶角色
? ? * root
### 用戶創(chuàng)建
```
>? db.createUser({user: "jhh",pwd: "pwd",roles: [{role:"readWrite",db:"sms"}]})
Successfully added user: {
? ? "user" : "jhh",
? ? "roles" : [
? ? ? ? {
? ? ? ? ? ? "role" : "readWrite",
? ? ? ? ? ? "db" : "sms"
? ? ? ? }
? ? ]
}
```
> 補(bǔ)充:查看用戶授權(quán)情況可以在admin庫(kù)下通過(guò)db.system.users.find({user:"user_name"})查看。
### **用戶的作用范圍**
在admin 數(shù)據(jù)庫(kù)中創(chuàng)建的角色,作用范圍是全局的,能夠在admin,其他數(shù)據(jù)庫(kù)中使用,并且能夠繼承其他數(shù)據(jù)庫(kù)的角色;而在非admin中創(chuàng)建的角色,作用范圍是當(dāng)前數(shù)據(jù)庫(kù),只能在當(dāng)前DB中使用,只能繼承當(dāng)前數(shù)據(jù)庫(kù)的角色。
# MongoDB備份與恢復(fù)
MongoDB官方提供了兩套數(shù)據(jù)導(dǎo)入導(dǎo)出工具:一般來(lái)說(shuō),進(jìn)行整庫(kù)導(dǎo)出導(dǎo)入時(shí)使用mongodump和mongostore,這一對(duì)組合操作的數(shù)據(jù)是BSON格式,進(jìn)行大量dump和restore時(shí)效率較高。進(jìn)行單個(gè)集合導(dǎo)出導(dǎo)入時(shí)使用mongoexport和mongoimport,這一對(duì)組合操作的數(shù)據(jù)是JSON格式,可讀性較高。

## 數(shù)據(jù)庫(kù)備份與恢復(fù)
* mongodump腳本語(yǔ)法:mongodump -h dbhost -d dbname -o dbdirectory
? ? * -h:
? ? ? ? MongDB所在服務(wù)器地址,例如:127.0.0.1,當(dāng)然也可以指定端口號(hào):127.0.0.1:27017
? ? * -d:
? ? ? ? 需要備份的數(shù)據(jù)庫(kù)實(shí)例,例如:test
? ? * -o**:**
? ? ? ? 備份的數(shù)據(jù)存放位置,例如:\data\dump,當(dāng)然該目錄需要提前建立,在備份完成后,系統(tǒng)自動(dòng)在dump目錄下建立一個(gè)test目錄,這個(gè)目錄里面存放該數(shù)據(jù)庫(kù)實(shí)例的備份數(shù)據(jù)。
* mongorestore腳本語(yǔ)法:mongorestore -h <hostname><:port> -d dbname <path>
? ? * --host <:port>, -h <:port>:
? ? ? ? MongoDB所在服務(wù)器地址,默認(rèn)為: localhost:27017
? ? * --db , -d :
? ? ? ? 需要恢復(fù)的數(shù)據(jù)庫(kù)實(shí)例,例如:test,當(dāng)然這個(gè)名稱也可以和備份時(shí)候的不一樣,比如test2
? ? * --drop:
? ? ? ? 恢復(fù)的時(shí)候,先刪除當(dāng)前數(shù)據(jù),然后恢復(fù)備份的數(shù)據(jù)。
? ? * <path>:
? ? ? ? mongorestore 最后的一個(gè)參數(shù),設(shè)置備份數(shù)據(jù)所在位置,例如:\data\dump\test。
? ? ? ? 你不能同時(shí)指定 <path> 和 --dir 選項(xiàng),--dir也可以設(shè)置備份目錄。
? ? * --dir:
? ? ? ? 指定備份的目錄
? ? ? ? 你不能同時(shí)指定 <path> 和 --dir 選項(xiàng)。
## 集合導(dǎo)入與導(dǎo)出
* mongoexport語(yǔ)法:mongoexport -d dbname -c collectionname -o file --type json/csv -f field
? ? * -h:代表遠(yuǎn)程連接的數(shù)據(jù)庫(kù)地址,默認(rèn)連接本地Mongo數(shù)據(jù)庫(kù)
? ? * --port:代表遠(yuǎn)程連接的數(shù)據(jù)庫(kù)的端口,默認(rèn)連接的遠(yuǎn)程端口27017
? ? * -d :數(shù)據(jù)庫(kù)名
? ? *? -c :collection名
? ? *? -o :輸出的文件名,導(dǎo)出成功后文件會(huì)生成在當(dāng)前執(zhí)行路徑下。
? ? *? --type : 輸出的格式,默認(rèn)為json
? ? *? -f :輸出的字段,如果-type為csv,則需要加上-f "字段名"。
? ? * -q:代表查詢條件
? ? * -limit:讀取指定數(shù)量的數(shù)據(jù)記錄
例:從sit環(huán)境數(shù)據(jù)數(shù)據(jù)庫(kù)導(dǎo)出sms庫(kù)里seller集合任意5文檔到本地。
```
~ ? mongoexport -h=10.4.12.78 -d=sms -c=seller -o=seller.json
--limit=5 -u sit-user -p cDk%KVJA4qA5 --authenticationDatabase=admin --query '{logistics:"auto"}'
2018-07-24T12:50:18.992+0800 connected to: 10.4.12.78
2018-07-24T12:50:19.037+0800 exported 5 records
```
> 注意:這里一定要加—authenticationDatabase=admin指定use和password作為admin庫(kù)的驗(yàn)證,順序直接跟在-u和-p參數(shù)之后,否則會(huì)作為 sms庫(kù)的驗(yàn)證,導(dǎo)致驗(yàn)證失?。ú聹y(cè)sit-user是admin庫(kù)下全局用戶,在sms的庫(kù)用戶中不存在)。具體見(jiàn)“角色作用范圍”一節(jié)。
* mongoimport語(yǔ)法:mongoimport -d dbname -c collectionname --file filename --headerline --type json/csv -f field
? ? *? -d :數(shù)據(jù)庫(kù)名
? ? *? -c :collection名
? ? *? --type :導(dǎo)入的格式默認(rèn)json
? ? *? -f :導(dǎo)入的字段名,type為json格式時(shí)不能指定。
? ? *? --headerline :如果導(dǎo)入的格式是csv,則可以使用第一行的標(biāo)題作為導(dǎo)入的字段
? ? *? --file :要導(dǎo)入的文件
例:將上面導(dǎo)出的seller.json文件導(dǎo)入到本地?cái)?shù)據(jù)庫(kù)sms下。
```
~ ? mongoimport -d sms? -c seller --file seller.json --type json
2018-07-24T13:39:05.380+0800? ? connected to: localhost
2018-07-24T13:39:05.394+0800? ? imported 5 documents
```
客戶端查看seller集合文檔條數(shù):
```
導(dǎo)入執(zhí)行前:
> db.seller.find().count();
2
導(dǎo)入執(zhí)行后:
> db.seller.find().count();
7
```
# 數(shù)據(jù)庫(kù)與緩存之間更新機(jī)制
## **緩存同步的常用模式**
緩存同步的模式,可以按照緩存的用途(主要用于讀或者寫(xiě))分為兩類(lèi):讀緩存的同步和寫(xiě)緩存的同步。
讀緩存的同步:
### **緩存預(yù)加載模式**
提前將數(shù)據(jù)從數(shù)據(jù)庫(kù)加載到緩存,如果數(shù)據(jù)庫(kù)有寫(xiě)更新,同步更新緩存。
### **緩存直讀模式**
應(yīng)用先查看緩存中是否有該數(shù)據(jù),有則直接使用,如果沒(méi)有,從數(shù)據(jù)庫(kù)加載,然后放入緩存,下次以后再訪問(wèn)就可以直接從緩存中獲得。
### **緩存直寫(xiě)模式**
在數(shù)據(jù)更新時(shí),同時(shí)寫(xiě)入緩存和數(shù)據(jù)庫(kù)。這種模式是最穩(wěn)妥的辦法,但是性能會(huì)受到一定的影響。

其中的過(guò)程是這樣的:
1.檢查用戶請(qǐng)求的數(shù)據(jù)是緩存中是否有存在,如果有存在的話,只需要直接把請(qǐng)求的數(shù)據(jù)返回,無(wú)需查詢數(shù)據(jù)庫(kù)。
2.如果請(qǐng)求的數(shù)據(jù)在緩存中找不到,這時(shí)候再去查詢數(shù)據(jù)庫(kù)。返回請(qǐng)求數(shù)據(jù)的同時(shí),把數(shù)據(jù)存儲(chǔ)到緩存中一份。
3.保持緩存的“新鮮性”,每當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候(比如,數(shù)據(jù)有被修改,或被刪除的情況下),要同步的更新緩存信息,確保用戶不會(huì)在緩存取到舊的數(shù)據(jù)。