【MongoDB系列】:MongoDB 集群,副本集模式(二)
2019獨角獸企業重金招聘Python工程師標準>>>
mongoDB官方已經不建議使用主從模式了,替代方案是采用副本集的模式。詳情
副本集
使用復制可以將數據副本保存到多臺服務器上,這是生產環境必須使用的。使用MongoDB得復制功能。即時一臺或者多臺服務器出錯,也可以保證應用程序正常運行和數據安全。
在MongoDB中,創建一個副本集后就可以使用復制功能。副本集是一組服務器,其中有一個主服務器(primary),用戶處理客戶端請求;還有多個備份服務器(secondary),用于保存主服務器的數據副本。如果主服務器奔潰了,備份服務器會自動將其中一個成員升級為新的主服務器。
使用副本集,我們就可以解決上篇博客遺留的第一個問題。我們來看看mongoDB副本集的架構圖:
由圖可以看到客戶端連接到整個副本集,不關心具體哪一臺機器是否掛掉。主服務器負責整個副本集的讀寫,副本集定期同步數據備份,一但主節點掛掉,副本節點就會選舉一個新的主服務器,這一切對于應用服務器不需要關心。我們看一下主服務器掛掉后的架構:
副本集中的副本節點在主節點掛掉后通過心跳機制檢測到后,就會在集群內發起主節點的選舉機制,自動選舉一位新的主服務器。
官方推薦的副本集機器數量為至少3個,那我們也按照這個數量配置測試。我們的測試依舊是在本機。
1、解壓mongodb-win32-x86_64-2008plus-3.0.7.zip文件,重命名為mongodb_1、mongodb_2、mongodb_3
2、分別在目錄下載創建data文件夾,再data文件下創建db和log文件夾。
3、創建批處理文件start_mongodb_1.bat、start_mongodb_2.bat、start_mongodb_3.bat
mongodb_1\bin\mongod?--replSet?repset?--port?5001?--dbpath?mongodb_1\data\db?--logpath=mongodb_1\data\log\r1.log?--logappend mongodb_2\bin\mongod?--replSet?repset?--port?5002?--dbpath?mongodb_2\data\db?--logpath=mongodb_2\data\log\r2.log?--logappend mongodb_3\bin\mongod?--replSet?repset?--port?5003?--dbpath?mongodb_3\data\db?--logpath=mongodb_3\data\log\r3.log?--logappend4、分別啟動MongoDB。
5、查看狀態
提示錯誤信息,這是因為副本集還有沒有進行初始化操作。
6、初始化副本集
登錄任意一套MongoDB服務器,我默認登錄5001
#定義副本集配置變量,這里的 _id:”rs1” 和上面命令參數“ –replSet rs1” 要保持一樣。因為我登錄得是5001,所以默認就是rs1
config?=?{?_id:"repset",?members:[ {_id:0,host:"127.0.0.1:5001"}, {_id:1,host:"127.0.0.1:5002"}, {_id:2,host:"127.0.0.1:5003"}] }輸出:
{"_id"?:?"rs1","members"?:?[{"_id"?:?0,"host"?:?"127.0.0.1:5001"},{"_id"?:?1,"host"?:?"127.0.0.1:5002"},{"_id"?:?2,"host"?:?"127.0.0.1:5003"}] }開始初始化副本集
輸出成功
查看狀態
repset:OTHER>?rs.status(); {"set"?:?"repset","date"?:?ISODate("2015-12-07T09:17:37.503Z"),"myState"?:?1,"members"?:?[{"_id"?:?0,"name"?:?"127.0.0.1:5001","health"?:?1,"state"?:?1,"stateStr"?:?"PRIMARY","uptime"?:?859,"optime"?:?Timestamp(1449479764,?1),"optimeDate"?:?ISODate("2015-12-07T09:16:04Z"),"electionTime"?:?Timestamp(1449479769,?1),"electionDate"?:?ISODate("2015-12-07T09:16:09Z"),"configVersion"?:?1,"self"?:?true},{"_id"?:?1,"name"?:?"127.0.0.1:5002","health"?:?1,"state"?:?2,"stateStr"?:?"SECONDARY","uptime"?:?683,"optime"?:?Timestamp(1449479764,?1),"optimeDate"?:?ISODate("2015-12-07T09:16:04Z"),"lastHeartbeat"?:?ISODate("2015-12-07T09:17:37.177Z"),"lastHeartbeatRecv"?:?ISODate("2015-12-07T09:17:35.909Z" ),"pingMs"?:?0,"configVersion"?:?1},{"_id"?:?2,"name"?:?"127.0.0.1:5003","health"?:?1,"state"?:?2,"stateStr"?:?"SECONDARY","uptime"?:?683,"optime"?:?Timestamp(1449479764,?1),"optimeDate"?:?ISODate("2015-12-07T09:16:04Z"),"lastHeartbeat"?:?ISODate("2015-12-07T09:17:35.747Z"),"lastHeartbeatRecv"?:?ISODate("2015-12-07T09:17:35.747Z" ),"pingMs"?:?0,"configVersion"?:?1}],"ok"?:?1 } repset:PRIMARY>我們可以到5001為主節點(primary),5002、5003為副本節點(secondary),這樣我們的副本集就搭建成功了。
7、測試副本集復制功能
7.1 連接主節點,插入一條數據 ?
mongo?127.0.0.1:5001 repset:PRIMARY>?db.user.insert({"userName":"test1","age":25}) WriteResult({?"nInserted"?:?1?}) repset:PRIMARY>?db.user.find() {?"_id"?:?ObjectId("566550638db08caa9ace63f6"),?"userName"?:?"test1",?"age"?:?25}7.2 連接副本節點
上面報錯,這是因為mongodb默認是從主節點讀寫數據的,副本節點上不允許讀,需要設置副本節點可以讀。
?db.getMongo().setSlaveOk(); repset:SECONDARY>?db.user.find() {?"_id"?:?ObjectId("566550638db08caa9ace63f6"),?"userName"?:?"test1",?"age"?:?25}這樣我們就讀取到數據。
測試副本集故障轉移功能
1、我們先停掉主節點,即端口5001,然后連接到5001
我們發現服務器被停止,已經無法連接到5001上。
2、連接到5002,發現不受影響
C:\Users\hanfeng>mongo?127.0.0.1:5002 2015-12-08T09:42:27.633+0800?I?CONTROL??Hotfix?KB2731284?or?later?update?is?not installed,?will?zero-out?data?files MongoDB?shell?version:?3.0.7 connecting?to:?127.0.0.1:5002/test3、查看節點狀態
我們發現5001狀態確實是被停止掉,5002正常,5003已經被選舉為主節點了。
4、接著我們在啟動5001端口服務,我們會發現5001已經變成從節點了。
JAVA測試
三個節點有一個節點掛掉也不會影響應用程序客戶端對整個副本集的讀寫!
package?com.hanfeng.test; import?java.util.List; import?org.bson.Document; import?org.junit.BeforeClass; import?org.junit.Test; import?org.slf4j.Logger; import?org.slf4j.LoggerFactory; import?com.google.common.collect.Lists; import?com.mongodb.BasicDBObject; import?com.mongodb.MongoClient; import?com.mongodb.ServerAddress; import?com.mongodb.client.FindIterable; import?com.mongodb.client.MongoCollection; import?com.mongodb.client.MongoCursor; import?com.mongodb.client.MongoDatabase; /***? *?@Title:?TestMongoDBReplSet.java? *?@Package?com.hanfeng.test? *?@Description:?TODO *?@author?hanfeng?? *?@date?2015年12月8日?上午10:35:06? *?@version?V1.0??? *?@Copyright:?2011-2015?xxx.cn?Inc.?All?rights?reserved.*/ public?class?TestMongoDBReplSet?{ private?static?Logger?log?=LoggerFactory.getLogger(TestMongoDBReplSet.class); private?static?MongoClient?client; private?static?MongoDatabase?db; @BeforeClass public?static?void?init(){ List<ServerAddress>?addresses?=?Lists.newArrayList(); addresses.add(new?ServerAddress("127.0.0.1",?5001)); addresses.add(new?ServerAddress("127.0.0.1",?5002)); addresses.add(new?ServerAddress("127.0.0.1",?5003)); client?=?new?MongoClient(addresses);db?=?client.getDatabase("test"); } /***?插入數據*/ @Test public?void?testInsert()?{ log.debug("begin?Test?insert?data");MongoCollection<Document>?collection?=?db.getCollection("user");//?插入Document?doc?=?new?Document();doc.put("userName",?"測試用戶");doc.put("age",?200);collection.insertOne(doc);?? } /***?查詢數據*/ @Test public?void?testFind(){MongoCollection<Document>?collection?=?db.getCollection("user");FindIterable<Document>?iterable?=?collection.find();MongoCursor<Document>?cursor?=?iterable.iterator();while?(cursor.hasNext())?{Document?user?=?cursor.next();log.debug("檢索的數據:"+user.getString("userName")+":"+user.getInteger("age"));}?? } /***?更新數據*/ @Test public?void?testUpdate(){MongoCollection<Document>?collection?=?db.getCollection("user");BasicDBObject?newDocument?=?new?BasicDBObject();??//更新一個特定的值,使用?$set?來處理,不然會把整個文檔替換掉newDocument.append("$set",?new?BasicDBObject().append("age",?30));??BasicDBObject?searchQuery?=?new?BasicDBObject().append("userName",?"test1");??collection.updateOne(searchQuery,?newDocument);? } /***?刪除數據*/ @Test public?void?testDelete(){MongoCollection<Document>?collection?=?db.getCollection("user");BasicDBObject?query?=?new?BasicDBObject("userName",?"測試用戶");collection.deleteMany(query); } }到這里我們已經完成了副本集得架構搭建,那么這個架構是否完美呢?再這個架構上,我們的讀寫是在一起的,那么該如何解決讀寫壓力過大問題,我們最常用的解決辦法就是讀寫分離,那MongoDB得讀寫分離是如何進行的?我們接著來解決這個問題吧。
副本集讀寫分離
我們來改造下架構,如下
在大多數系統中,寫的操作永遠沒有讀的操作多,再3臺副本集中,我們其中的一臺主節點用來寫操作,兩臺從節點負責讀取操作。
1、設置讀寫分離需要先在副本節點SECONDARY 設置 setSlaveOk。
C:\Users\hanfeng>mongo?127.0.0.1:5001 2015-12-08T10:46:21.756+0800?I?CONTROL??Hotfix?KB2731284?or?later?update?is?not installed,?will?zero-out?data?files MongoDB?shell?version:?3.0.7 connecting?to:?127.0.0.1:5001/test repset:SECONDARY>?db.user.find() Error:?error:?{?"$err"?:?"not?master?and?slaveOk=false",?"code"?:?13435?} repset:SECONDARY>?db.getMongo().setSlaveOk(); repset:SECONDARY>?db.user.find() {?"_id"?:?ObjectId("566550638db08caa9ace63f6"),?"userName"?:?"test1",?"age"?:?30} {?"_id"?:?ObjectId("56663f6c1e2f251e14ddf934"),?"userName"?:?"測試用戶",?"age"?:200?} repset:SECONDARY>2、測試讀寫分離
//讀操作從副本節點讀取 ReadPreference?preference?=?ReadPreference.secondary(); client.setReadPreference(preference);讀參數除了secondary一共還有五個參數:primary、primaryPreferred、secondary、secondaryPreferred、nearest。
primary:默認參數,只從主節點上進行讀取操作;
primaryPreferred:大部分從主節點上讀取數據,只有主節點不可用時從secondary節點讀取數據。
secondary:只從secondary節點上進行讀取操作,存在的問題是secondary節點的數據會比primary節點數據“舊”。
secondaryPreferred:優先從secondary節點進行讀取操作,secondary節點不可用時從主節點讀取數據;
nearest:不管是主節點、secondary節點,從網絡延遲最低的節點上讀取數據。
好,讀寫分離做好我們可以數據分流,減輕壓力解決了“主節點的讀寫壓力過大如何解決?”這個問題。不過當我們的副本節點增多時,主節點的復制壓力會加大有什么辦法解決嗎?mongodb早就有了相應的解決方案。
其中的仲裁節點不存儲數據,只是負責故障轉移的群體投票,這樣就少了數據復制的壓力。是不是想得很周到啊,一看mongodb的開發兄弟熟知大數據架構體系,其實不只是主節點、副本節點、仲裁節點,還有Secondary-Only、Hidden、Delayed、Non-Voting。
Secondary-Only:不能成為primary節點,只能作為secondary副本節點,防止一些性能不高的節點成為主節點。
Hidden:這類節點是不能夠被客戶端制定IP引用,也不能被設置為主節點,但是可以投票,一般用于備份數據。
Delayed:可以指定一個時間延遲從primary節點同步數據。主要用于備份數據,如果實時同步,誤刪除數據馬上同步到從節點,恢復又恢復不了。
Non-Voting:沒有選舉權的secondary節點,純粹的備份數據節點。
到此整個mongodb副本集搞定了兩個問題:
主節點掛了能否自動切換連接?目前需要手工切換。
主節點的讀寫壓力過大如何解決?
還有這兩個問題后續解決:
從節點每個上面的數據都是對數據庫全量拷貝,從節點壓力會不會過大?
數據壓力大到機器支撐不了的時候能否做到自動擴展?
做了副本集發現又一些問題:
副本集故障轉移,主節點是如何選舉的?能否手動干涉下架某一臺主節點。
官方說副本集數量最好是奇數,為什么?
mongodb副本集是如何同步的?如果同步不及時會出現什么情況?會不會出現不一致性?
mongodb的故障轉移會不會無故自動發生?什么條件會觸發?頻繁觸發可能會帶來系統負載加重
參考:http://www.lanceyan.com/tech/mongodb/mongodb_repset1.html
??????????https://docs.mongodb.org/manual/
轉載于:https://my.oschina.net/ihanfeng/blog/540623
總結
以上是生活随笔為你收集整理的【MongoDB系列】:MongoDB 集群,副本集模式(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mybatis源码解析(五) --- t
- 下一篇: Gitter - 高颜值GitHub小程