關於mongodb的學習與探索二

chengkai發表於2017-12-15

前言

在上一篇文章中我們瞭解到了mongodb的基本操作,這一章我們更進一步繼續來探討mongodb,廢話不多說,直接入正題吧。

mongodb的高階用法

聚合

mongodb中聚合(aggregate)主要用於處理資料(諸如統計平均值,求和等),並返回計算後的資料結果。有點類似sql語句中的count(*).

例如現在有個需求,有一個儲存這文章的集合,你可能希望找出發表文章最多的那個作者,假設每篇文章被儲存為mongodb中的一個文件,這個需求我們怎麼去實現呢?下面我們就一步一步的來實現這個需求。

aggregate()的用法

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

假設集合中的資料如下:

{ "_id" : ObjectId("5a338a31ae83f6d96d3ef220"), "title" : "mongodb", "author" : "ck" }
{ "_id" : ObjectId("5a338a38ae83f6d96d3ef221"), "title" : "java", "author" : "ck" }
{ "_id" : ObjectId("5a338a4dae83f6d96d3ef222"), "title" : "node", "author" : "st" }
複製程式碼

首先我們先計算出每個作者縮寫的文章數並排序,使用aggregate()計算結果如下所示:

> db.col.aggregate([{ $group: { _id: "$author", num: { $sum : 1 } } },{ $sort : { "_id":1 } }])
{ "_id" : "ck", "num" : 2 }
{ "_id" : "st", "num" : 1 }
複製程式碼

直接一步到位的如下所示:

> db.col.aggregate([{ $group: { _id: "$author", num: { $sum : 1 } } },{ $sort : { "_id":1 } }, { $limit:1 }])
{ "_id" : "ck", "num" : 2 }
複製程式碼

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

表示式 描述 例項
$sum 計算總和 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$sum : “$likes”}}}])。
$avg 計算平均值 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$avg : “$likes”}}}])
$min 獲取集合中所有文件對應值得最小值。 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$min : “$likes”}}}])。
$max 獲取集合中所有文件對應值得最大值 db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$max : “$likes”}}}])
$push 在結果文件中插入值到一個陣列中。 db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$push: “$url”}}}])
$addToSet 在結果文件中插入值到一個陣列中,但不建立副本。 db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$addToSet : “$url”}}}])
$first 根據資源文件的排序獲取第一個文件資料。 db.mycol.aggregate([{$group : {_id : “$by_user”, first_url : {$first : “$url”}}}])。
$last 根據資源文件的排序獲取最後一個文件資料 db.mycol.aggregate([{$group : {_id : “$by_user”, last_url : {$last : “$url”}}}])
管道的概念

管道在Unix和Linux中一般用於將當前命令的輸出結果作為下一個命令的引數。
MongoDB的聚合管道將MongoDB文件在一個管道處理完畢後將結果傳遞給下一個管道處理。管道操作是可以重複的。
表示式:處理輸入文件並輸出。表示式是無狀態的,只能用於計算當前聚合管道的文件,不能處理其它的文件。
這裡我們介紹一下聚合框架中常用的幾個操作:

$project:修改輸入文件的結構。可以用來重新命名、增加或刪除域,也可以用於建立計算結果以及巢狀文件。

$match:用於過濾資料,只輸出符合條件的文件。$match使用MongoDB的標準查詢操作。

$limit:用來限制MongoDB聚合管道返回的文件數。

$skip:在聚合管道中跳過指定數量的文件,並返回餘下的文件。

$unwind:將文件中的某一個陣列型別欄位拆分成多條,每條包含陣列中的一個值。

$group:將集合中的文件分組,可用於統計結果。

$sort:將輸入文件排序後輸出。

$geoNear:輸出接近某一地理位置的有序文件。

管道操作符例項

1、$project例項

db.article.aggregate(
    { $project : {
        title : 1 ,
        author : 1 ,
    }}
 );
複製程式碼

這樣的話結果中就只還有_id,tilte和author三個欄位了,預設情況下_id欄位是被包含的,如果要想不包含_id話可以這樣:

db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});
複製程式碼

2.$match例項

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

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

3.$skip例項

db.article.aggregate(
    { $skip : 5 });
複製程式碼

經過$skip管道操作符處理後,前五個文件被”過濾”掉。

複製(副本集)

複製集簡介

目前我們一直使用的是單臺伺服器,一個mongod伺服器程式。如果只是用作學習和開發,這是可以的,但是如果用到生產環境中,風險會很高。如果伺服器崩潰了或者不可訪問了怎麼辦?資料庫至少會有一段時間不可用。如果是硬體除了問題,可能需要將資料轉移到另一個機器上。在最壞的情況下,磁碟或者網路問題可能會導致資料損壞或者資料不可訪問。

使用複製可以將資料副本儲存到多臺伺服器上,建議在所有的生產環境中都要使用。使用mongodb的複製功能,即使一臺或多臺伺服器出錯,也可以保證應用程式正常執行和資料安全。

複製集功能

保障資料的安全性

資料高可用性

災難恢復

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

分散式讀取資料

複製集特性

1.資料的一致性:主節點是唯一的,沒有mysql那樣的雙主結構,如果當前主節點出現故障後,叢集會自動容災,通過選舉,來選舉出來一個合適的從節點來充當新的主節點,所以主節點是唯一的,但不是固定的。

2.大多數原則:叢集存貨節點小於等於二分之一時叢集則不可寫,只可讀。如果複製集的伺服器掛了一般就沒法選舉了,將全部將為從節點。

3.從庫無法寫入:只有主節點可寫入資料,從節點只可讀。

關於複製集以及下面的分片這一塊,想深入研究的同學可以自行學習,原諒我只是個前端,哈哈。。

分片

分片簡介

分片是指將資料拆分,將其分散存放在不同的機器上的過程。有時也用分割槽來表示這個概念。將資料分散到不同的機器上,不需要功能強大的大型計算機就可以儲存更多的資料,處理更大的負載。

幾乎所有資料庫軟體都能進行手動分片。應用需要維護與若干不同資料伺服器的連結,每個連結還是完全獨立的。應用程式管理不同伺服器上不同資料的儲存,還管理在合適的資料上查詢資料的工作。這種方法可以很好的工作,但是非常難以維護,比如向叢集新增節點或從叢集刪除節點都很款南,調整資料分佈和負載模式也不輕鬆。

mongodb支援自動分片,可以使資料庫架構對應用程式不可見,也可以簡化系統管理。對應用程式而言,好像始終在使用一個單機的mongodb伺服器一樣。另一方面,mongodb自動處理資料在分片上的分部,也更容易新增和刪除分片。

不管從開發角度還是運營角度來說,分片都是最困難最複雜的mongodb配置方式。

理解叢集的元件

mongodb的分片機制允許你建立一個包含許多臺機器(分片)的叢集,將資料子集分散在叢集中,每個分片維護者一個資料集合的子集。與單機伺服器和副本集相比,使用叢集架構可以使應用程式具有更大的資料處理能力。

這裡有一點要注意,許多人可能會混淆複製和分片的概念。記住,複製是讓多臺伺服器都擁有同樣的資料副本,每一臺伺服器都是其他伺服器的映象,而每一個分片都有其他分片擁有不同的資料子集。

分片的目標之一是建立一個擁有5臺、10臺甚至1000臺機器的叢集,整個叢集對應用程式來說就像是一臺單機伺服器。為了對應用程式隱藏資料庫架構的細節,在分片之前要先執行mongos進行一次路由過程。這個路由伺服器維護著一個“內容列表”,指明瞭每個分片包含什麼資料內容。應用程式只需要連線到路由伺服器,就可以像使用單機伺服器一樣進行正常的請求了,如下圖所示。路由伺服器知道哪些資料位於哪個分片,可以將請求轉發給相應的分片。每個分片對請求的相應都會傳送給路由伺服器,路由伺服器將所有相應合併在一起,返回給應用程式。對應用程式來說,它只知道自己是連線到了一臺單機mongod伺服器,如下圖所示:

1-1
1-2

mongodb的安全加固

眾所周知,資料安全非常重要,那麼我們應該怎麼對我們的mongodb進行安全加固呢?

修改預設埠

修改預設的mongodb埠(預設為:27017)為其他埠。

不要把mongodb伺服器直接部署在外網上

1.通過安全組防火牆或伺服器作業系統配置的防火牆對訪問源IP進行控制,如果僅對內網伺服器提供服務,建議禁止將mongodb服務釋出到網際網路上。

使用—bind_ip選項。

該選項可以限制監聽介面IP,當在啟動mongodb的時候,使用 –bind_ip 192.168.0.1 表示啟動IP地址繫結,資料庫例項將只監聽192.168.0.1的請求。

啟動基於角色的登入認證功能。

在admin資料庫中建立使用者,如使用者名稱supper,密碼supWDxsf67%H(此處為舉例說明,請勿使用此賬號密碼)。

1.在未開啟認證的環境下,登入到資料庫。

> [mongodb@rac3 bin]$ ./mongo 127.0.0.1:27028 (此處修改了預設埠)
MongoDB shell version: 2.0.1
connecting to: 127.0.0.1:27028/test
複製程式碼

2.切換到admin資料庫

> use admin
switched to db admin
複製程式碼

3.建立管理員賬號

> db.addUser("supper", "supWDxsf67%H")或
> db.createUser({user:"supper",pwd:"supWDxsf67%H",roles:["root"]})
{ "n" : 0, "connectionId" : 4, "err" : null, "ok" : 1 }
{
"user" : "supper",
"readOnly" : false,
"pwd" : "51a481f72b8b8218df9fee50b3737c44",
"_id" : ObjectId("4f2bc0d357a309043c6947a4")
}
複製程式碼

管理員賬號將在system.users中。

> db.getCollectionNames()
[ "system.indexes", "system.users", "system.version" ]
複製程式碼

說明:

mongodb從v3版本開始取消使用addUser方法,採用db.createUser方法建立使用者;

賬號不要設定為常見賬號,密碼需要滿足一定的複雜度,長度至少八位以上,幷包括大小寫字母、數字、特殊字元混合體,不要使用生日、姓名、身份證編號等常見密碼。

4.驗證使用者是否建立成功。

> db.auth("supper","supWDxsf67%H")
> exit "system.version" ]
bye
複製程式碼

5.結束程式,重啟MongoDB服務。

> ./mongod --dbpath=/path/mongodb --bind_ip=192.168.0.1 --port=27028 --fork=true logpath=/path/mongod.log &
複製程式碼

說明:

    • admin.system.users中將會儲存比在其它資料庫中設定的使用者許可權更大的使用者資訊,擁有超級許可權,也就是說在admin中建立的使用者可以對mongodb中的其他資料庫資料進行操作。
    • MongoDB系統中,資料庫是由超級使用者來建立的,一個資料庫可以包含多個使用者,一個使用者只能在一個資料庫下,不同資料庫中的使用者可以同名。
    • 特定資料庫(比如DB1)的使用者User1,不能夠訪問其他資料庫DB2,但是可以訪問本資料庫下其他使用者建立的資料。
    • 不同資料庫中同名的使用者不能夠登入其他資料庫,比如DB1、DB2都有user1,以user1登入DB1後,不能夠登入到DB2進行資料庫操作。
    • 在admin資料庫建立的使用者具有超級許可權,可以對mongodb系統內的任何資料庫的資料物件進行操作。
    • 使用db.auth()可以對資料庫中的使用者進行驗證,如果驗證成功則返回1,否則返回0。 db.auth()只能針對登入使用者所屬的資料庫的使用者資訊進行驗證,不能驗證其他資料庫的使用者資訊。
禁用HTTP和REST埠。

MongoDB自身帶有一個HTTP服務和並支援REST介面(在V2.6以後這些介面預設是關閉的)。MongoDB預設使用預設埠監聽Web服務,一般不需要通過Web方式進行遠端管理,建議禁用。
修改配置檔案或在啟動的時候選擇–nohttpinterface引數即可。

nohttpinterface = false
複製程式碼
開啟日誌審計功能。

審計功能可以用來記錄使用者對資料庫的所有相關操作。這些記錄可以讓系統管理員在需要的時候分析資料庫在什麼時段發生了什麼事情。

使用SSL加密功能。

MongoDB叢集之間以及從客戶端連線到MongoDB例項的連線應該使用SSL。使用SSL對效能沒有影響並且可以防範類似於man-in-the-middle的攻擊。

注意MongoDB社群版預設並不支援SSL。您可以選用MongoDB企業版(支援SSL),或者從原始碼重新編譯MongoDB並使用—ssl選項來獲得SSL功能。

以上所有配置,推薦以配置檔案形式儲存配置。

[mongodb@rac3 bin]$ vim /path/mongod.conf
port=27028-------埠。預設為27017埠,MongoDB的預設服務TCP埠,監聽客戶端連線。要是埠設定小於1024,比如1021,則需要root許可權啟動,不能用mongodb帳號啟動,(普通帳號即使是27017也起不來)否則報錯:[mongo --port=1021 連線]
bind_ip=192.168.0.1------繫結地址。預設127.0.0.1,只能通過本地連線。程式繫結和監聽來自這個地址上的應用連線。要是需要給其他伺服器連線,則需要註釋掉這個或則把IP改成本機地址,如192.168.200.201[其他伺服器用 mongo --host=192.168.200.201 連線] ,可以用一個逗號分隔的列表繫結多個IP地址。
logpath=/path/mongod.log------開啟日誌審計功能,此項為日誌檔案路徑,可以自定義指定。
pidfilepath=/path/mongod.pid------程式ID,沒有指定則啟動時候就沒有PID檔案。
auth=true------使用者認證,預設false。不需要認證。當設定為true時候,進入資料庫需要auth驗證,當資料庫裡沒有使用者,則不需要驗證也可以操作。直到建立了第一個使用者,之後操作都需要驗證。
logappend=true------寫日誌的模式:設定為true為追加。預設是覆蓋。如果未指定此設定,啟動時MongoDB的將覆蓋現有的日誌檔案。
fork=true------是否後臺執行,設定為true 啟動 程式在後臺執行的守護程式模式。預設false。
nohttpinterface = false------是否禁止http介面,即28017 埠開啟的服務。預設false,支援。
複製程式碼

然後,啟動MongoDB服務時載入配置檔案。

[mongodb@rac3 bin]$ ./mongod -f /path/mongod.conf
複製程式碼
對業務關鍵敏感資料進行加密儲存。

建議您梳理業務資料,對關鍵的敏感資料加密後入庫,例如:賬號、密碼、郵箱地址、手機號碼、身份ID等其他資料。加密演算法推薦選擇國際通用加密演算法和多次加鹽組合自定義演算法,防止加密演算法被破解。

即使黑客獲取資料後,也檢視不了資料,通過“看不懂”的資料加密方式將損失降到最低。

對資料進行本地異地備份。

完善的備份策略是保證資料安全的最後一根救命稻草。

推薦:可靠的本地備份+遠端備份儲存方案

本地備份

1.MongoDB備份方式

>mongodump -h dbhost -d dbname -o dbdirectory
-h:
MongDB所在伺服器地址,例如:127.0.0.1,當然也可以指定埠號:127.0.0.1:27017
-d:
需要備份的資料庫例項,例如:test
-o:
備份的資料存放位置,例如:c:datadump,該目錄需要提前建立,在備份完成後,系統自動在dump目錄下建立一個test目錄,這個目錄裡面存放該資料庫例項的備份資料。
複製程式碼

2.MongoDB資料恢復

mongodb使用 mongorestore 命令來恢復備份的資料。
語法
mongorestore命令指令碼語法如下:
>mongorestore -h dbhost -d dbname --directoryperdb dbdirectory
-h:
MongoDB所在伺服器地址
-d:
需要恢復的資料庫例項,例如:test,這個名稱也可以和備份時候的不一樣,比如test2。
--directoryperdb:
備份資料所在位置,例如:c:datadump	est。
--drop:
恢復的時候,先刪除當前資料,然後恢復備份的資料。就是說,恢復後,備份後新增修改的資料都會被刪除,慎用!
複製程式碼

3.Mongodump命令可選引數列表如下所示。

語法 描述 例項
mongodump –host HOST_NAME –port PORT_NUMBER 該命令將備份所有mongodb資料 mongodump –host w3cschool.cc –port 27017
mongodump –dbpath DB_PATH –out BACKUP_DIRECTORY mongodump –dbpath /data/db/ –out /data/backup
mongodump –collection COLLECTION –db DB_NAME 該命令將備份指定資料庫的集合 mongodump –collection mycol –db test

4.備份策略

全量備份:可以最快的時間快速恢復所有資料,缺點是備份成本大,時間長

全量備份+增量備份:可以較快的恢復所有資料,缺點是恢復時間長,如果增量資料有問題,無法恢復所有資料。

搭建從庫:直接切換到從庫,前提是從庫的資料安全可靠。

歡迎各路大佬對文章錯誤的地方進行指正,萬分感謝,共同學習進步。博主的GitHub地址,可以在GitHub提Issues或者直接在文章下面評論區留言。

相關文章