部落格已經搬家https://www.tianmingxing.com
MongoDB是一個使用C++編寫的、開源的、面向文檔的NoSQL(Not Only SQL)資料庫,也是當前最熱門的NoSql資料庫之一。 NoSQL簡介
NoSQL的意思是“不僅僅是SQL”,是目前流行的“非關係型資料庫”的統稱。常見的NoSQL資料庫如:Redis、CouchDB、MongoDB、HBase、Cassandra等。 背景
出現NoSQL的原因:為解決在Web2.0時代出現的三高要求: 對資料庫高並發讀寫的需求 對海量資料的高效率儲存和訪問的需求 對資料庫的高可擴充性和高可用性的需求 而RDB裡面的一些特性,在web2.0裡面往往變得不那麼重要,比如:
資料庫事務一致性 資料庫的即時讀寫 複雜的SQL查詢,特別是多表關聯查詢
從第4點可以看出MongoDB的使用情境,但具體的我在後面重點介紹。 CAP定理
又被稱作布魯爾定理(Eric Brewer)它指出對於一個分散式運算系統來說,不可能同時滿足以下三點: 強一致性(Consistency):系統在執行某項操作後資料狀態仍然處於一致,例如在分布式系統中,更新操作執行成功後所有的使用者都應該讀取到最新的值,這樣的系統被認為具有強一致性。 可用性(Availability):每一個操作總是能夠在一定的時間內返回結果 分區容錯性(Partition tolerance):單個節點故障不應導致整個系統崩潰,也就是說儘管網路在節點之間丟棄(或延遲)任意數量的訊息,但是系統繼續操作。 根據CAP原理將資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類 CA:單點叢集,滿足一致性,可用性,通常在可擴充性上不太強大,比如RDB CP:滿足一致性和分區容錯性,通常效能不是特別高,如分散式資料庫 AP:滿足可用性和分區容錯性,通常可能對一致性要求低一些,如大多數的NoSQL BASE(Basically Available,Soft-state,Eventual consistency)
eBay的架構師Dan Pritchett源於對大規模分布式系統的實踐總結,在ACM上發表文章提出BASE理論,BASE理論是對CAP理論的延伸,核心思想是即使無法做到強一致性(Strong Consistency,CAP的一致性就是強一致性),但應用可以採用適合的方式達到最終一致性(Eventual Consitency)。 基本可用(Basically Available):系統能夠基本運行並一直提供服務 軟狀態(Soft-state):系統不要求一直保持強一致狀態 最終一致性(Eventual consistency):系統需要在某一時刻後達到一致性要求 NoSQL的特點 優點 擴充簡單方便,尤其是水平橫向擴充(縱向擴充是指用更強的機器;橫向擴充是指把資料分散到多個機器) 讀寫快速高效,多數都會映射到記憶體操作 成本低廉,用普通機器,分布式叢集即可 資料模型靈活,沒有固定的資料模型 缺點 不提供對SQL的支援 對事務操作的支援較弱 MongoDB使用情境
相信有很多人瞭解之後都會覺得,nosql好是好但我該在哪些情況下使用,又如何與實際項目結合呢。接下來我舉幾個例子,大家自己體會體會。 遊戲情境,使用 MongoDB 儲存遊戲使用者資訊,使用者的裝備、積分等直接以內嵌文檔的形式儲存,方便查詢、更新 物流情境,使用 MongoDB 儲存訂單資訊,訂單狀態在運送過程中會不斷更新,以 MongoDB 內嵌數組的形式來儲存,一次查詢就能將訂單所有的變更讀取出來。 社交情境,使用 MongoDB 儲存儲存使用者資訊,以及使用者發表的朋友圈資訊,通過地理位置索引實現附近的人、地點等功能 物聯網情境,使用 MongoDB 儲存所有接入的智慧型裝置資訊,以及裝置彙報的日誌資訊,並對這些資訊進行多維度分析 ApsaraVideo for Live,使用 MongoDB 儲存使用者資訊、禮物資訊等
並沒有某個業務情境必須要使用 MongoDB才能解決,但使用 MongoDB 通常能讓你以更低的成本解決問題(包括學習、開發、營運等成本),下面是 MongoDB 的主要特性,大家可以對照自己的業務需求看看,匹配的越多,用 MongoDB 就越合適。
MongoDB 特性 |
優勢 |
事務支援 |
MongoDB 目前只支援單文檔事務,需要複雜事務支援的情境暫時不適合 |
靈活的文檔模型 |
JSON 格式儲存最接近真實物件模型,對開發人員友好,方便快速開發迭代 |
高可用複製集 |
滿足資料高可靠、服務高可用的需求,營運簡單,故障自動切換 |
可擴充分區叢集 |
海量資料存放區,服務能力水平擴充 |
高效能 |
mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支援滿足各種情境需求 |
強大的索引支援 |
地理位置索引可用於構建 各種 O2O 應用、文本索引解決搜尋的需求、TTL索引解決曆史資料自動到期的需求 |
Gridfs |
解決檔案儲存體的需求 |
aggregation & mapreduce |
解決資料分析情境需求,使用者可以自己寫查詢語句或指令碼,將請求都分發到 MongoDB 上完成 |
安裝
mongodb的安裝方式比較簡單,下面我示範在CentOS7上用源碼和yum兩種方式安裝。 使用yum方式安裝
在實際生產環境中伺服器上的軟體通常都由營運人員安裝,我們通過此種傻瓜式安裝是為了方便現在學習。
整個mongodb(社區版)包含如下軟體
軟體名稱 |
描述 |
mongodb-org-server |
包含mongod精靈和關聯的配置和init指令碼 |
mongodb-org-mongos |
包含mongos精靈 |
mongodb-org-shell |
包含mongo shell,它是一個串連mongodb的命令列用戶端,允許使用者直接輸入nosql文法管理資料庫。 |
mongodb-org-tools |
包含以下工具的MongoDB:資料匯入、匯出、備份、恢複等等 |
建立yum源檔案
vim /etc/yum.repos.d/mongodb-org-3.4.repo
把下面的內容複寫到上面的檔案中
[mongodb-org-3.4]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/gpgcheck=1enabled=1gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
啟動yum命令開始安裝
yum install -y mongodb-org
如果使用SELinux,則必須配置SELinux,以允許在基於Red Hat Linux的系統(Red Hat Enterprise Linux或CentOS Linux)上啟動MongoDB。
vim /etc/selinux/config#在開啟的檔案中將值設定為disabledSELINUX=disabled
啟動mongodb
#centos6用這種方式service mongod start#centos7用這種方式systemctl start mongod
檢查進程是否成功啟動
可以通過查看mongodb的記錄檔來判斷,開啟 /var/log/mongodb/mongod.log 查看裡面有無內容。我們可以將mongodb服務設定為開機就啟動 chkconfig mongod on。
其它控制命令
#停止mongodb服務service mongod stop#重啟mongodbservice mongod restart
命令列用戶端串連到MongoDB服務
./bin/mongo#上面的命令輸入後直接斷行符號,你應該可以看到和我相同的提示root@bogon mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongoMongoDB shell version v3.4.1connecting to: mongodb://127.0.0.1:27017MongoDB server version: 3.4.1Welcome to the MongoDB shell.#我們可以輸入一個命令測試一下,現在教你一個最簡單的命令,查看當前資料庫有哪些> show dbsadmin 0.000GBlocal 0.000GB#可以看到有兩個預設的資料庫,如果要退出用戶端可以輸入> exitbye
到這裡第一種方式的安裝就結束了 使用源碼安裝
營運人員都比較喜歡源碼安裝,他們覺得可控性會比較強,但現在公司項目中都使用雲端服務,所以作為開發人員直接連接上就可以用了。我這裡使用的源碼包實際上是二進位包,也就是說已經被官方編譯過了,所以下載二進位包的時候一定要確定你用的作業系統,否則包是停用。 下載二進位安裝包並解壓
cd /usr/local/srcwget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.1.tgzgunzip mongodb-linux-x86_64-rhel70-3.4.1.tgztar -xvf mongodb-linux-x86_64-rhel70-3.4.1.tar#你現在應該可以看到和我相同的檔案目錄.├── bin│ ├── bsondump│ ├── mongo│ ├── mongod│ ├── mongodump│ ├── mongoexport│ ├── mongofiles│ ├── mongoimport│ ├── mongooplog│ ├── mongoperf│ ├── mongoreplay│ ├── mongorestore│ ├── mongos│ ├── mongostat│ └── mongotop├── GNU-AGPL-3.0├── MPL-2├── README└── THIRD-PARTY-NOTICES
建立資料存放區目錄,預設是在 /data/db,如果這個目錄不存在則啟動時報錯。你可以自由建立一個目錄,在啟動時指定即可。
mkdir -p data/db1#開始啟動服務./bin/mongod --dbpath=data/db1#你應該可以看到和我相同的結果017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.2017-01-08T13:54:55.379+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'2017-01-08T13:54:55.379+0800 I CONTROL [initandlisten]2017-01-08T13:54:55.446+0800 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory 'data/db1/diagnostic.data'2017-01-08T13:54:55.447+0800 I NETWORK [thread1] waiting for connections on port 27017
細心的你應該發現一個問題,當前服務是前台啟動的,如果你想做後續操作那必須再開啟一個終端。這樣肯定麻煩,那我們也可以採用後台啟動的,在啟動時必須指定一個記錄檔。
mkdir logstouch logs/db1.log./bin/mongod --dbpath=data/db1 --fork --logpath=logs/db1.log
到這裡源碼安裝並啟動mongodb就結束了,下面對啟動時的可配置參數作一個介紹。 基本配置
--quiet # 安靜輸出--port arg # 指定服務連接埠號碼,預設連接埠27017--bind_ip arg # 綁定服務IP,若綁定127.0.0.1,則只能本機訪問,不指定預設本地所有IP--logpath arg # 指定MongoDB記錄檔,注意是指定檔案不是目錄--logappend # 使用追加的方式寫日誌--pidfilepath arg # PID File 的完整路徑,如果沒有設定,則沒有PID檔案--keyFile arg # 叢集的私密金鑰的完整路徑,只對於Replica Set 架構有效--unixSocketPrefix arg # UNIX域通訊端替代目錄,(預設為 /tmp)--fork # 以守護進程的方式運行MongoDB,建立伺服器處理序--auth # 啟用驗證--cpu # 定期顯示CPU的CPU利用率和iowait--dbpath arg # 指定資料庫路徑--diaglog arg # diaglog選項 0=off 1=W 2=R 3=both 7=W+some reads--directoryperdb # 設定每個資料庫將被儲存在一個單獨的目錄--journal # 啟用日誌選項,MongoDB的資料操作將會寫入到journal檔案夾的檔案裡--journalOptions arg # 啟用日誌診斷選項--ipv6 # 啟用IPv6選項--jsonp # 允許JSONP形式通過HTTP訪問(有安全影響)--maxConns arg # 最大同時串連數 預設2000--noauth # 不啟用驗證--nohttpinterface # 關閉http介面,預設關閉27018連接埠訪問--noprealloc # 禁用資料檔案預分配(往往影響效能)--noscripting # 禁用指令碼引擎--notablescan # 不允許表掃描--nounixsocket # 禁用Unix通訊端監聽--nssize arg (=16) # 設定信資料庫.ns檔案大小(MB)--objcheck # 在收到客戶資料,檢查的有效性,--profile arg # 檔案參數 0=off 1=slow, 2=all--quota # 限制每個資料庫的檔案數,設定預設為8--quotaFiles arg # number of files allower per db, requires --quota--rest # 開啟簡單的rest API--repair # 修複所有資料庫run repair on all dbs--repairpath arg # 修複庫產生的檔案的目錄,預設為目錄名稱dbpath--slowms arg (=100) # value of slow for profile and console log--smallfiles # 使用較小的預設檔案--syncdelay arg (=60) # 資料寫入磁碟的時間秒數(0=never,不推薦)--sysinfo # 列印一些診斷系統資訊--upgrade # 如果需要升級資料庫
Replicaton 參數
--fastsync # 從一個dbpath裡啟用從庫複製服務,該dbpath的資料庫是主庫的快照,可用於快速啟用同步--autoresync # 如果從庫與主庫同步資料差得多,自動重新同步,--oplogSize arg # 設定oplog的大小(MB)
主、從參數
--master # 主庫模式--slave # 從庫模式--source arg # 從庫 連接埠號碼--only arg # 指定單一的資料庫複寫--slavedelay arg # 設定從庫同步主庫的延遲時間
複本集
--replSet arg # 設定複本集名稱
分區
--configsvr # 聲明這是一個叢集的config服務,預設連接埠27019,預設目錄/data/configdb--shardsvr # 聲明這是一個叢集的分區,預設連接埠27018--noMoveParanoia # 關閉偏執為moveChunk資料儲存
卸載
如果你想卸載上面安裝mongodb,那麼可以參考下面的步驟。當然這個需求在實際部署時不會有,但是作為我們練習也是有用的。
要從系統中完全刪除MongoDB,你必須刪除MongoDB應用程式本身,設定檔和任何包含資料和日誌的目錄。此過程無法復原,因此請確保在繼續之前備份所有配置和資料。
yum erase $(rpm -qa | grep mongodb-org)rm -r /var/log/mongodbrm -r /var/lib/mongo
如果是採用源碼(二進位)包安裝,刪除解壓出來的目錄即可(因為我們上面把資料和日誌都放進去了),如果你指定在其它位置,那找到相應位置刪除即可。 MongoDB基本使用 基本概念 資料庫
MongoDB的一個執行個體可以擁有一個或多個相互獨立的資料庫,每個資料庫都有自己的集合(表)。 集合
集合可以看作是擁有動態模式的表 方檔
文檔是MongoDB中基本的資料單元,類似於RDB的行。文檔是索引值對的一個有序集合。在JS中,文檔被表示成對象。 _id
每個文檔都有個特殊的 _id,在文檔所屬集合中是唯一的,預設由mongodb自己維護,當然你也可以自己指定。 JavaScript shell
MongoDB內建了一個功能強大的JavaScript Shell,可以用於管理或操作MongoDB MongoDB和RDB的概念對比
都有資料庫的概念 集合 –>RDB的表 文檔 –>RDB表中的一條記錄 文檔對象裡面的 key –> RDB表中的欄位 文檔對象裡面的 value–> RDB表中欄位的值 MongoDB中沒有外鍵的概念 資料庫名稱定義規則
1:不能是空串2:不得含有/、\、。、$、空格、Null 字元等等,基本只能使用ASCII中的字母和數字3:區分大小寫,建議全部小寫4:最多為64位元組5:不得使用保留的資料庫名,比如:admin,local,config
注意:資料庫最終會成為檔案,資料庫名就是檔案的名稱 集合名稱定義規則
1:不能是空串2:不能包含\0字元(Null 字元),這個字元表示集合名的結束,也不能包含”$”3:不能以”system.”開頭,這是為系統集合保留的首碼
文檔的鍵的定義規則
1:不能包含\0字元(Null 字元),這個字元表示鍵的結束2:“.”和“$”是被保留的,只能在特定環境下用3:區分類型,同時也區分大小寫4:鍵不能重複
注意:文檔的索引值對是有順序的,相同的索引值對如果有不同順序的話,也是不同的文檔 MongoDB基本的資料類型
資料類型 |
描述 |
舉例 |
null |
表示空值或者未定義的對象 |
{“x”:null} |
布爾值 |
真或者假:true或者false |
{“x”:true} |
32位整數 |
shell不支援該類型,預設會轉換成64位浮點數,也可以使用NumberInt類 |
{“x”:NumberInt(“3”)} |
64位整數 |
shell不支援該類型,預設會轉換成64位浮點數,也可以使用NumberLong類 |
{“x”:NumberLong(“3”)} |
64位浮點數 |
shell中的數字就是這一種類型 |
{“x”:3.14,”y”:3} |
字串 |
UTF-8字串 |
{“foo”:”bar”} |
符號 |
shell不支援,shell會將資料庫中的符號類型的資料自動轉換成字串 |
|
對象id |
文檔的12位元組的唯一id |
{“id”: ObjectId()} |
日期 |
從標準紀元開始的毫秒數 |
{“date”:new Date()} |
Regex |
文檔中可以包含Regex,遵循JavaScript的文法 |
{“foo”:/foobar/i} |
代碼 |
文檔中可以包含JavaScript代碼 |
{“x”:function() {}} |
未定義 |
undefined |
{“x”:undefined} |
數組 |
值的集合或者列表 |
{“arr”: [“a”,”b”]} |
內嵌文檔 |
文檔可以作為文檔中某個key的value |
{“x”:{“foo”:”bar”}} |
增刪改(CUD)操作
接下來我在命令列用戶端中操作資料,這裡假設你已經進入命令列。如果你沒有與我保持同步,請再看一看上面的教程。
# 運行shell,命令:mongo ip:port,在這裡我使用的預設的連接埠所以就沒有指定[root@bogon mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongo# 顯示現有的資料庫,命令:show dbs;> show dbsadmin 0.000GBlocal 0.000GB# 顯示當前使用的資料庫,命令:db> dbtest# 切換當前使用的資料庫,命令:use 資料庫名稱> use localswitched to db local# 建立資料庫:MongoDB沒有專門建立資料庫的語句,可以使用“use” 來使用某個資料庫,如果要使用的資料庫不存在,那麼將會建立一個,會在真正向該庫加入文檔後,儲存成為檔案。> use mallswitched to db mall# 刪除資料庫,命令:db.dropDatabase(),在使用這個命令前要記得切換到將被刪除的資料,可以用上面學到的命令確認一下。> dbmall> db.dropDatabase(){ "dropped" : "mall", "ok" : 1 }# 顯示現有的集合,命令:show collections,我們切換到一個新的資料庫,到裡面插入一個集合并儲存一條資料,這樣做只是為了造些語句便於示範這個命令。> use mallswitched to db mall> db.user.insert({name:'張三'})WriteResult({ "nInserted" : 1 })> show collectionsuser# 建立集合:在MongoDB中不用建立集合,因為沒有固定的結構,直接使用db.集合名稱.命令 來操作就可以了。如果非要顯示建立集合的話,用:db.createCollecion('集合名稱');> db.createCollection('system'){ "ok" : 1 }# 插入並儲存文檔# insert方法,可以單獨插入一個文檔,也可以插入多個,用“[ ]”即可。注意:# 1:MongoDB會為每個沒有“_id”欄位的文檔自動添加一個”_id”欄位# 2:每個Doc必須小於16MB# 上面已經有示範,這裡就不再贅述# 刪除文檔,命令:remove , 可以按條件來刪除只是刪除文檔,集合還在,如果使用 drop命令,會連帶集合和索引都刪掉# 查看集合中所有的文檔,命令:db.集合名稱.find();> db.user.find(){ "_id" : ObjectId("5871e5f99423674edcea4eec"), "name" : "張三" }> db.user.remove({})WriteResult({ "nRemoved" : 1 })> db.user.find()># 查看集合的狀態資訊:db.集合名.stats();> db.user.stats(){ "ns" : "mall.user", "size" : 0, "count" : 0, "storageSize" : 20480, "capped" : false, "wiredTiger" : {...}, ...}# 查看集合中第一個文檔,命令:db.集合名稱.findOne({條件對象}),造一些資料方便測試> db.user.insert([{name:'李四'},{name:'王五'}])BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ]})> db.user.findOne(){ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }# 文檔替換,命令: db.集合名稱. update(條件,新的文檔);> db.user.update({name:'王五'}, {name:'王五',sex:'男'})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.find(){ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }# 上面的操作說明:先找到名稱是王五的這一條記錄,然後用新文檔替換資料原來的所有值,是一個整體替換的操作。# save方法# 如果文檔存在就更新,不存在就建立,主要根據”_id”來判斷。可以看到我們插入了一個名稱相同的文檔,結果是成功的。> db.user.save({name:'李四'})WriteResult({ "nInserted" : 1 })> db.user.find(){ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }# upsert# 找到了合格文檔就更新,否則會以這個條件和更新文檔來建立一個新文檔。指定update方法的第三個參數為true,可表示是upsert> db.user.update({name:"張三"}, {name:"張三",sex:'女'})WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })> db.user.find(){ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }{