前言
MongoDB是一個由C++語言編寫的基于分布式檔案存儲的資料庫,是目前NoSQL資料庫中比較熱門的一種,旨在為Web應用提供可擴充的高性能資料存儲解決方案。本文介紹MongoDB複制集及資料分片。
MongoDB
簡介
MongoDB是一個介于關系資料庫和非關系資料庫之間的産品,是非關系資料庫當中功能最豐富,最像關系資料庫的。支援的資料結構非常松散,是以可以存儲比較複雜的資料類型。最大的特點是其支援的查詢語言非常強大,其文法有點類似于面向對象的查詢語言,幾乎可以實作類似關系資料庫單表查詢的絕大部分功能,而且還支援對資料建立索引。
特點及功能特性
特點:高性能、易部署、易使用,存儲資料非常友善
主要功能特性有:
面向集合存儲,易存儲對象類型的資料
模式自由
支援動态查詢
支援完全索引,包含内部對象
支援查詢
支援複制和故障恢複
使用高效的二進制資料存儲,包括大型對象(如視訊等)
自動處理碎片,以支援雲計算層次的擴充性
支援Ruby,Python,Java,C++,PHP等多種語言
檔案存儲格式為Bson(一種Json的擴充)
可通過網絡通路
優缺點
與關系型資料庫相比,MongoDB的優點:
弱一緻性(最終一緻),更能保證使用者的通路速度
文檔結構的存儲方式,能夠更便捷的擷取資料
内置GridFS,支援大容量的存儲
内置Sharding
第三方支援豐富(這是與其他的NoSQL相比,MongoDB也具有的優勢)
性能優越
與關系型資料庫相比,MongoDB的缺點:
不支援事務操作
占用空間過大
沒有成熟的維護工具
MongoDB複制集
複制集
工作特性:
至少三個,且應該為奇數個節點,可使用arbiter(仲裁者)來參與選舉
複制集可實作失效自動轉移(通過選舉方式實作)
複制集的中特殊類型的節點:
0優先級的節點:冷備節點,不會被選舉成為主節點,但可以參與選舉
被隐藏的從節點:首先是一個0優先級的從節點,且對用戶端不可見
延遲複制的從節點:首先是一個0優先級的從節點,且複制時間落後于主節點一個固定時長
arbiter: 仲裁者
複制集架構
<a href="http://s3.51cto.com/wyfs02/M01/6F/A3/wKiom1WjeEzBZzG-AAFIL7klLVc870.jpg" target="_blank"></a>
實驗拓撲
1
2
<code>#系統環境:CentOS6.6</code>
<code>#各節點時間已同步</code>
配置過程
安裝所需軟體
3
4
5
6
7
<code>[root@node1 ~]</code><code># cd mongodb/</code>
<code>[root@node1 mongodb]</code><code># ls</code>
<code>mongodb-org-server-2.6.10-1.x86_64.rpm mongodb-org-tools-2.6.10-1.x86_64.rpm</code>
<code>mongodb-org-shell-2.6.10-1.x86_64.rpm</code>
<code>[root@node1 mongodb]</code><code># yum install *.rpm -y #3個包都安裝</code>
<code>#所有節點都執行以上安裝操作</code>
編輯配置檔案
8
9
10
11
12
<code>[root@node1 ~]</code><code># vim /etc/mongod.conf </code>
<code>logpath=</code><code>/var/log/mongodb/mongod</code><code>.log</code>
<code>logappend=</code><code>true</code>
<code>fork=</code><code>true</code>
<code>dbpath=</code><code>/mongodb/data</code> <code>#資料位置</code>
<code>pidfilepath=</code><code>/var/run/mongodb/mongod</code><code>.pid</code>
<code>#bind_ip=127.0.0.1 #預設監聽本機,注釋掉監聽所有</code>
<code>httpinterface=</code><code>true</code> <code>#開放web</code>
<code>rest=</code><code>true</code>
<code>replSet=testSet </code><code>#複制集名,可自定義</code>
<code>replIndexPrefetch=_id_only</code>
同步配置檔案至各節點
<code>[root@node1 ~]</code><code># scp /etc/mongod.conf node3:/etc</code>
<code>root@node3's password: </code>
<code>mongod.conf 100% 1567 1.5KB</code><code>/s</code> <code>00:00 </code>
<code>[root@node1 ~]</code><code># scp /etc/mongod.conf node4:/etc</code>
<code>root@node4's password: </code>
<code>mongod.conf 100% 1567 1.5KB</code><code>/s</code> <code>00:00</code>
建立資料目錄
<code>[root@node1 ~]</code><code># mkdir /mongodb/data -pv</code>
<code>mkdir</code><code>: created directory `</code><code>/mongodb</code><code>'</code>
<code>mkdir</code><code>: created directory `</code><code>/mongodb/data</code><code>'</code>
<code>[root@node1 ~]</code><code># chown -R mongod.mongod /mongodb</code>
<code>#各節點都執行以上操作</code>
啟動服務
<code>[root@node1 ~]</code><code># service mongod start</code>
<code>Starting mongod: [ OK ]</code>
<code>[root@node1 mongodb]</code><code># ss -tnl | grep 27017</code>
<code>LISTEN 0 128 *:27017 *:* </code>
<code>#各節點啟動服務,啟動過程中需要初始化資料,故啟動較慢</code>
連接配接資料庫做初始化
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<code>[root@node1 ~]</code><code># mongo</code>
<code>MongoDB shell version: 2.6.10</code>
<code>connecting to: </code><code>test</code>
<code>Welcome to the MongoDB shell.</code>
<code>For interactive help, </code><code>type</code> <code>"help"</code><code>.</code>
<code>For </code><code>more</code> <code>comprehensive documentation, see</code>
<code> </code><code>http:</code><code>//docs</code><code>.mongodb.org/</code>
<code>Questions? Try the support group</code>
<code> </code><code>http:</code><code>//groups</code><code>.google.com</code><code>/group/mongodb-user</code>
<code>> rs.initiate()</code>
<code>{</code>
<code> </code><code>"info2"</code> <code>: </code><code>"no configuration explicitly specified -- making one"</code><code>,</code>
<code> </code><code>"me"</code> <code>: </code><code>"node1.scholar.com:27017"</code><code>,</code>
<code> </code><code>"info"</code> <code>: </code><code>"Config now saved locally. Should come online in about a minute."</code><code>,</code>
<code> </code><code>"ok"</code> <code>: 1</code>
<code>}</code>
<code>#初始化成功,檢視狀态資訊</code>
<code>> rs.status()</code>
<code> </code><code>"set"</code> <code>: </code><code>"testSet"</code><code>,</code>
<code> </code><code>"date"</code> <code>: ISODate(</code><code>"2015-07-13T12:33:27Z"</code><code>),</code>
<code> </code><code>"myState"</code> <code>: 1,</code>
<code> </code><code>"members"</code> <code>: [</code>
<code> </code><code>{</code>
<code> </code><code>"_id"</code> <code>: 0,</code>
<code> </code><code>"name"</code> <code>: </code><code>"node1.scholar.com:27017"</code><code>,</code>
<code> </code><code>"health"</code> <code>: 1,</code>
<code> </code><code>"state"</code> <code>: 1,</code>
<code> </code><code>"stateStr"</code> <code>: </code><code>"PRIMARY"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 1111,</code>
<code> </code><code>"optime"</code> <code>: Timestamp(1436790736, 1),</code>
<code> </code><code>"optimeDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:32:16Z"</code><code>),</code>
<code> </code><code>"electionTime"</code> <code>: Timestamp(1436790737, 1),</code>
<code> </code><code>"electionDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:32:17Z"</code><code>),</code>
<code> </code><code>"self"</code> <code>: </code><code>true</code>
<code> </code><code>}</code>
<code> </code><code>],</code>
<code>testSet:PRIMARY> </code>
<code>#已成為主節點</code>
添加節點
44
45
46
47
48
49
50
51
52
53
54
55
56
<code>testSet:PRIMARY> rs.add(</code><code>"172.16.10.125"</code><code>)</code>
<code>{ </code><code>"ok"</code> <code>: 1 }</code>
<code>testSet:PRIMARY> rs.add(</code><code>"172.16.10.126"</code><code>)</code>
<code>#檢視各節點狀态</code>
<code>testSet:PRIMARY> rs.status()</code>
<code> </code><code>"date"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:07Z"</code><code>),</code>
<code> </code><code>"uptime"</code> <code>: 1571,</code>
<code> </code><code>"optime"</code> <code>: Timestamp(1436791178, 1),</code>
<code> </code><code>"optimeDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:39:38Z"</code><code>),</code>
<code> </code><code>},</code>
<code> </code><code>"_id"</code> <code>: 1,</code>
<code> </code><code>"name"</code> <code>: </code><code>"172.16.10.125:27017"</code><code>,</code>
<code> </code><code>"state"</code> <code>: 2,</code>
<code> </code><code>"stateStr"</code> <code>: </code><code>"SECONDARY"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 98,</code>
<code> </code><code>"lastHeartbeat"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:06Z"</code><code>),</code>
<code> </code><code>"lastHeartbeatRecv"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:05Z"</code><code>),</code>
<code> </code><code>"pingMs"</code> <code>: 1,</code>
<code> </code><code>"syncingTo"</code> <code>: </code><code>"node1.scholar.com:27017"</code>
<code> </code><code>"_id"</code> <code>: 2,</code>
<code> </code><code>"name"</code> <code>: </code><code>"172.16.10.126:27017"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 89,</code>
<code> </code><code>"lastHeartbeatRecv"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:06Z"</code><code>),</code>
建立資料,驗證是否同步
<code>#主節點</code>
<code>testSet:PRIMARY> use testdb</code>
<code>switched to db testdb</code>
<code>testSet:PRIMARY> db.students.insert({name: </code><code>"ZhangSan"</code><code>,age: </code><code>"21"</code><code>})</code>
<code>WriteResult({ </code><code>"nInserted"</code> <code>: 1 })</code>
<code>#從節點</code>
<code>testSet:SECONDARY> rs.slaveOk() </code><code>#每個從節點首先申明從節點準備完畢才可同步</code>
<code>testSet:SECONDARY> use testdb</code>
<code>testSet:SECONDARY> show collections</code>
<code>students</code>
<code>system.indexes</code>
<code>testSet:SECONDARY> db.students.findOne()</code>
<code> </code><code>"_id"</code> <code>: ObjectId(</code><code>"55a3b494ebcafd9edbdfce4d"</code><code>),</code>
<code> </code><code>"name"</code> <code>: </code><code>"ZhangSan"</code><code>,</code>
<code> </code><code>"age"</code> <code>: </code><code>"21"</code>
<code>#驗證從節點是否可寫</code>
<code>testSet:SECONDARY> db.classes.insert({class: </code><code>"2"</code><code>,numstu: </code><code>"50"</code><code>})</code>
<code>WriteResult({ </code><code>"writeError"</code> <code>: { </code><code>"code"</code> <code>: undefined, </code><code>"errmsg"</code> <code>: </code><code>"not master"</code> <code>} })</code>
<code>#由此可見,隻有主節點才可寫</code>
以上便是複制集的相關配置,如果主節點故障,從節點會自動選舉出新的主節點,這裡就不再示範
資料分片
分片緣由
分片(sharding)是MongoDB用來将大型集合分割到不同伺服器(叢集)上所采用的方法。當單台伺服器CPU,Memory,IO等無法滿足需求,就需要将資料分片存放,減緩伺服器壓力。
分片架構
<a href="http://s3.51cto.com/wyfs02/M01/6F/A2/wKioL1WjvPuQ0HVeAAG3WbZrFqw217.jpg" target="_blank"></a>
<a href="http://s3.51cto.com/wyfs02/M02/6F/A2/wKioL1WjwLeTs5aJAACNKqEdgL8258.jpg" target="_blank"></a>
因為以上做過實驗我們首先來清理一下資料
<code>[root@node1 ~]</code><code># service mongod stop</code>
<code>Stopping mongod: [ OK ]</code>
<code>[root@node1 ~]</code><code># rm -rf /mongodb/data/*</code>
<code>#各節點都執行以上操作,若第一次做可忽略</code>
Config Server配置
<code>#安裝所需包</code>
<code>[root@node2 mongodb]</code><code># ls</code>
<code>[root@node2 mongodb]</code><code># yum install *.rpm -y</code>
修改配置檔案
<code>[root@node2 ~]</code><code># vim /etc//mongod.conf </code>
<code>dbpath=</code><code>/mongodb/data</code>
<code>configsvr=</code><code>true</code> <code>#開啟config server </code>
<code>#bind_ip=127.0.0.1 </code>
<code>httpinterface=</code><code>true</code>
<code>[root@node2 ~]</code><code># mkdir /mongodb/data -pv</code>
<code>[root@node2 ~]</code><code># chown -R mongod.mongod /mongodb</code>
<code>[root@node2 ~]</code><code># service mongod start</code>
<code>[root@node2 ~]</code><code># ss -tnl | grep 27019</code>
<code>LISTEN 0 128 *:27019 *:* </code>
<code>#監聽端口已發生改變,不再是27017</code>
Mongos配置
<code>#安裝所需包,Mongos節點隻裝此包即可,無需裝第一次實驗中的mongod的相關包</code>
<code>[root@node1 ~]</code><code># yum install mongodb-org-mongos-2.6.10-1.x86_64.rpm -y</code>
<code>[root@node1 ~]</code><code># mongos --configdb=172.16.10.124 --fork --logpath=/var/log/mongodb/mongos.log</code>
<code>2015-07-13T22:22:47.404+0800 warning: running with 1 config server should be </code><code>done</code> <code>only fo</code>
<code>r testing purposes and is not recommended </code><code>for</code> <code>production</code>
<code>about to fork child process, waiting </code><code>until</code> <code>server is ready </code><code>for</code> <code>connections.</code>
<code>forked process: 3583</code>
<code>child process started successfully, parent exiting</code>
<code>#--configdb指定config server --logpath指定日志位置 --fork背景運作</code>
Shard配置
<code>#以為我們第一次實驗安裝過軟體了,下面直接修改配置檔案</code>
<code>[root@node3 ~]</code><code># vim /etc/mongod.conf </code>
<code>dbpath=</code><code>/mongodb/data</code>
<code>[root@node3 ~]</code><code># service mongod start</code>
<code>#兩個shard節點都執行以上操作</code>
Mongos節點添加Shard節點
<code>[root@node1 ~]</code><code># mongo --host 172.16.10.123</code>
<code>connecting to: 172.16.10.123:27017</code><code>/test</code>
<code>mongos> sh.addShard(</code><code>"172.16.10.125"</code><code>)</code>
<code>{ </code><code>"shardAdded"</code> <code>: </code><code>"shard0000"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code>mongos> sh.addShard(</code><code>"172.16.10.126"</code><code>)</code>
<code>{ </code><code>"shardAdded"</code> <code>: </code><code>"shard0001"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code>mongos> sh.status()</code>
<code>--- Sharding Status --- </code>
<code> </code><code>sharding version: {</code>
<code> </code><code>"_id"</code> <code>: 1,</code>
<code> </code><code>"version"</code> <code>: 4,</code>
<code> </code><code>"minCompatibleVersion"</code> <code>: 4,</code>
<code> </code><code>"currentVersion"</code> <code>: 5,</code>
<code> </code><code>"clusterId"</code> <code>: ObjectId(</code><code>"55a3c9ba131b83ff44e19435"</code><code>)</code>
<code> </code><code>shards:</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"shard0000"</code><code>, </code><code>"host"</code> <code>: </code><code>"172.16.10.125:27017"</code> <code>}</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"shard0001"</code><code>, </code><code>"host"</code> <code>: </code><code>"172.16.10.126:27017"</code> <code>}</code>
<code> </code><code>databases:</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"admin"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>false</code><code>, </code><code>"primary"</code> <code>: </code><code>"config"</code> <code>}</code>
對所需對象啟用分片功能
<code>#對資料庫啟用sharding功能</code>
<code>mongos> sh.enableSharding(</code><code>"testdb"</code><code>)</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"test"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>false</code><code>, </code><code>"primary"</code> <code>: </code><code>"shard0000"</code> <code>}</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"testdb"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>true</code><code>, </code><code>"primary"</code> <code>: </code><code>"shard0000"</code> <code>}</code>
<code>#指定需要分片的Collection及索引</code>
<code>mongos> sh.shardCollection(</code><code>"testdb.students"</code><code>,{</code><code>"age"</code><code>: 1})</code>
<code>{ </code><code>"collectionsharded"</code> <code>: </code><code>"testdb.students"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code> </code><code>testdb.students</code>
<code> </code><code>shard key: { </code><code>"age"</code> <code>: 1 }</code>
<code> </code><code>chunks:</code>
<code> </code><code>shard0000 1</code>
<code> </code><code>{ </code><code>"age"</code> <code>: { </code><code>"$minKey"</code> <code>: 1 } } -->> { </code><code>"age"</code> <code>: { </code><code>"$maxKey"</code> <code>: 1 } }</code>
<code>on : shard0000 Timestamp(1, 0)</code>
分片功能已開啟,接下來我們手動建立資料來驗證是否會分片
<code>mongos> use testdb</code>
<code>mongos> </code><code>for</code> <code>(i=1;i<=100000;i++) db.students.insert({name:</code><code>"student"</code><code>+i,age:(i%120),address:</code><code>"China"</code><code>})</code>
<code>mongos> db.students.</code><code>find</code><code>().count()</code>
<code>100000</code>
<code> </code><code>shard0001 1</code>
<code> </code><code>shard0000 2</code>
<code> </code><code>{ </code><code>"age"</code> <code>: { </code><code>"$minKey"</code> <code>: 1 } } -->> { </code><code>"age"</code> <code>: 1 } on : shard0001 Timestamp(2, 0) </code>
<code> </code><code>{ </code><code>"age"</code> <code>: 1 } -->> { </code><code>"age"</code> <code>: 119 } on : shard0000 Timestamp(2, 2) </code>
<code> </code><code>{ </code><code>"age"</code> <code>: 119 } -->> { </code><code>"age"</code> <code>: { </code><code>"$maxKey"</code> <code>: 1 } } on : shard0000 Timestamp(2, 3)</code>
檢視資料狀态會發現資料已被分到不同shard上,至此,資料分片成功實作
The end
MongoDB複制集及資料分片就先說到這裡了,通過以上簡單應用可以看出,MongoDB在大資料處理方面比關系型資料庫略勝一籌,但由于MongoDB目前還處在發展階段,在實際生産環境中還有許多問題有待解決,不過相信在未來MongoDB會更加出色。以上僅為個人學習整理,如有錯漏,大神勿噴~~~
本文轉自 北城書生 51CTO部落格,原文連結:http://blog.51cto.com/scholar/1673939