搭建高可用的MongoDB集羣

搭建高可用的MongoDB集羣

[日期:2014-04-25] 來源:CSDN  作者:嚴瀾 [字體:  ]

  MongoDB公司原名10gen,創立於2007年,在2013年收到一筆2.31億美元的融資後,公司市值評估已增至10億美元級別,這個高度是知名開源公司Red Hat(創建於1993年)20年的奮鬥成果。

  高性能、易擴展一直是MongoDB的立足之本,同時規範的文檔和接口更讓其深受用戶喜愛,這一點從分析DB-Engines的得分結果不難看出——僅僅1年時間,MongoDB就完成了第7名到第五名的提升,得分就從124分上升至214分,上升值是第四名PotgreSQL的兩倍,同時當下與PostgreSQL的得分也只相差16分不到。

  

 

    MongoDB能以如此速度發展,很大程度上歸結於許多傳統關係數據庫已無法應對當下數據處理的擴展性需求,雖然它們久經考驗,並具備不錯的性能及穩定性。然而區別於以往的使用方法,許多NoSQL都有着自己的限制,從而也導致了入門難的問題。這裏我們爲大家分享 嚴瀾的博文——如何搭建高效的MongoDB集羣。

  以下爲博文

  深入副本集內部機制

  該系列文章的第一部分介紹了副本集的配置,這個部分將深入研究一下副本集的內部機制。還是帶着副本集的問題來看吧!

  副本集故障轉移,主節點是如何選舉的?能否手動干涉下架某一臺主節點。

  官方說副本集數量最好是奇數,爲什麼?

  MongDB副本集是如何同步的?如果同步不及時會出現什麼情況?會不會出現不一致性?

  MongDB的故障轉移會不會無故自動發生?什麼條件會觸發?頻繁觸發可能會帶來系統負載加重?

  Bully算法

  MongDB副本集故障轉移功能得益於它的選舉機制。選舉機制採用了Bully算法,可以很方便從分佈式節點中選出主節點。一個分佈式集羣架構中一般都有一個所謂的主節點,可以有很多用途,比如緩存機器節點元數據,作爲集羣的訪問入口等等。主節點有就有吧,我們幹嘛要什麼Bully算法?要明白這個我們先看看這兩種架構:

  指定主節點的架構,這種架構一般都會申明一個節點爲主節點,其他節點都是從節點,如我們常用的MySQL就是這樣。但是這樣架構我們在第一節說了整個集羣如果主節點掛掉了就得手工操作,上架一個新的主節點或者從從節點恢復數據,不太靈活。

  

mongodb4

 

  不指定主節點,集羣中的任意節點都可以成爲主節點。MongoDB也就是採用這種架構,一但主節點掛了其他從節點自動接替變成主節點。如下圖:

  

mongodb故障轉移

 

  好了,問題就在這個地方,既然所有節點都是一樣,一但主節點掛了,怎麼確定下一個主節點?這就是Bully算法解決的問題。

  那什麼是Bully算法,Bully算法是一種協調者(主節點)競選算法,主要思想是集羣的每個成員都可以聲明它是主節點並通知其他節點。別的節點可以選擇接受這個聲稱或是拒絕並進入主節點競爭。被其他所有節點接受的節點才能成爲主節點。節點按照一些屬性來判斷誰應該勝出。這個屬性可以是一個靜態ID,也可以是更新的度量像最近一次事務ID(最新的節點會勝出)。詳情請參考 NoSQL數據庫分佈式算法的協調者競選還有 維基百科的解釋。

  選舉

  那麼,MongDB是怎進行選舉的呢?官方這麼描述:

  We use a consensus protocol to pick a primary. Exact details will be spared here but that basic process is:

  get maxLocalOpOrdinal from each server.

  if a majority of servers are not up (from this server’s POV), remain in Secondary mode and stop.

  if the last op time seems very old, stop and await human intervention.

  else, using a consensus protocol, pick the server with the highest maxLocalOpOrdinal as the Primary.

  大致翻譯過來爲使用一致協議選擇主節點。基本步驟爲:

  得到每個服務器節點的最後操作時間戳。每個MongDB都有oplog機制記錄本機操作,方便和主服務器進行對比數據是否同步還可以用於錯誤恢復。

  如果集羣中大部分服務器down機了,保留活着的節點都爲secondary狀態並停止,不選舉了。

  如果集羣中選舉出來的主節點或者所有從節點最後一次同步時間看起來很舊,停止選舉等待人來操作。

  如果上面都沒有問題就選擇最後操作時間戳最新(保證數據是最新的)的服務器節點作爲主節點。

  這裏提到了一個一致協議(其實就是bully算法),這個和數據庫的一致性協議還是有些區別,一致協議主要強調的是通過一些機制保證大家達成共識;而一致性協議強調的是操作的順序一致性,比如同時讀寫一個數據會不會出現髒數據。一致協議在分佈式裏有一個經典的算法叫“Paxos算法”,後續再介紹。

  上面有個問題,就是所有從節點的最後操作時間都是一樣怎麼辦?就是誰先成爲主節點的時間最快就選誰。

  選舉觸發條件

  選舉不是什麼時刻都會被觸發的,有以下情況可以觸發。

  初始化一個副本集時。

  副本集和主節點斷開連接,可能是網絡問題。

  主節點掛掉。

  選舉還有個前提條件,參與選舉的節點數量必須大於副本集總節點數量的一半,如果已經小於一半了所有節點保持只讀狀態。日誌將會出現:

  can't see a majority of the set, relinquishing primary

  1. 主節點掛掉能否人爲干預?答案是肯定的。

  可以通過replSetStepDown命令下架主節點。這個命令可以登錄主節點使用

  db.adminCommand({replSetStepDown : 1})

  如果殺不掉可以使用強制開關

  db.adminCommand({replSetStepDown : 1, force : true})

  或者使用 rs.stepDown(120)也可以達到同樣的效果,中間的數字指不能在停止服務這段時間成爲主節點,單位爲秒。

  2. 設置一個從節點有比主節點有更高的優先級。

  先查看當前集羣中優先級,通過rs.conf()命令,默認優先級爲1是不顯示的,這裏標示出來

  [java] view plaincopyrs.conf();

  [java] view plaincopy{

  "_id" : "rs0",

  "version" : 9,

  "members" : [

  {

  "_id" : 0,

  "host" : "192.168.1.136:27017" },

  {

  "_id" : 1,

  "host" : "192.168.1.137:27017" },

  {

  "_id" : 2,

  "host" : "192.168.1.138:27017" }

  ]

  }

  如果不想讓一個從節點成爲主節點可以怎麼操作?

  使用rs.freeze(120)凍結指定的秒數不能選舉成爲主節點。

  按照上一篇設置節點爲Non-Voting類型。

  當主節點不能和大部分從節點通訊。把主機節點網線拔掉,嘿嘿:)

  優先級還可以這麼用,如果我們不想設置什麼hidden節點,就用secondary類型作爲備份節點也不想讓他成爲主節點怎麼辦?看下圖,共三個節點分佈在兩個數據中心,數據中心2的節點設置優先級爲0不能成爲主節點,但是可以參與選舉、數據複製。架構還是很靈活吧!

  

deeprepset1

 

  奇數

  官方推薦副本集的成員數量爲奇數,最多12個副本集節點,最多7個節點參與選舉。最多12個副本集節點是因爲沒必要一份數據複製那麼多份,備份太多反而增加了網絡負載和拖慢了集羣性能;而最多7個節點參與選舉是因爲內部選舉機制節點數量太多就會導致1分鐘內還選不出主節點,凡事只要適當就好。這個“12”、“7”數字還好,通過他們官方經過性能測試定義出來可以理解。具體還有哪些限制參考官方文檔 《 MongoDB Limits and Thresholds 》。 但是這裏一直沒搞懂整個集羣爲什麼要奇數,通過測試集羣的數量爲偶數也是可以運行的,參考這個文章http://www.itpub.net/thread-1740982-1-1.html。後來突然看了一篇 stackoverflow的文章終於頓悟了,mongodb本身設計的就是一個可以跨IDC的分佈式數據庫,所以我們應該把它放到大的環境來看。

  假設四個節點被分成兩個IDC,每個IDC各兩臺機器,如下圖。但這樣就出現了個問題,如果兩個IDC網絡斷掉,這在廣域網上很容易出現的問題,在上面選舉中提到只要主節點和集羣中大部分節點斷開鏈接就會開始一輪新的選舉操作,不過MongoDB副本集兩邊都只有兩個節點,但是選舉要求參與的節點數量必須大於一半,這樣所有集羣節點都沒辦法參與選舉,只會處於只讀狀態。但是如果是奇數節點就不會出現這個問題,假設3個節點,只要有2個節點活着就可以選舉,5箇中的3個,7箇中的4個……

  

deeprepset2

 

  心跳

  綜上所述,整個集羣需要保持一定的通信才能知道哪些節點活着哪些節點掛掉。MongoDB節點會向副本集中的其他節點每兩秒就會發送一次pings包,如果其他節點在10秒鐘之內沒有返回就標示爲不能訪問。每個節點內部都會維護一個狀態映射表,表明當前每個節點是什麼角色、日誌時間戳等關鍵信息。如果是主節點,除了維護映射表外還需要檢查自己能否和集羣中內大部分節點通訊,如果不能則把自己降級爲secondary只讀節點。

  同步

  副本集同步分爲初始化同步和keep複製。初始化同步指全量從主節點同步數據,如果主節點數據量比較大同步時間會比較長。而keep複製指初始化同步過後,節點之間的實時同步一般是增量同步。初始化同步不只是在第一次纔會被處罰,有以下兩種情況會觸發:

  secondary第一次加入,這個是肯定的。

  secondary落後的數據量超過了oplog的大小,這樣也會被全量複製。

  那什麼是oplog的大小?前面說過oplog保存了數據的操作記錄,secondary複製oplog並把裏面的操作在secondary執行一遍。但是oplog也是mongodb的一個集合,保存在local.oplog.rs裏;然而這個oplog是一個capped collection,也就是固定大小的集合,新數據加入超過集合的大小會覆蓋,所以這裏需要注意,跨IDC的複製要設置合適的oplogSize,避免在生產環境經常產生全量複製。oplogSize 可以通過–oplogSize設置大小,對於Linux 和Windows 64位,oplog size默認爲剩餘磁盤空間的5%。

  同步也並非只能從主節點同步,假設集羣中3個節點,節點1是主節點在IDC1,節點2、節點3在IDC2,初始化節點2、節點3會從節點1同步數據。後面節點2、節點3會使用就近原則從當前IDC的副本集中進行復制,只要有一個節點從IDC1的節點1複製數據。

  設置同步還要注意以下幾點:

  secondary不會從delayed和hidden成員上覆制數據。

  只要是需要同步,兩個成員的buildindexes必須要相同無論是否是true和false。buildindexes主要用來設置是否這個節點的數據用於查詢,默認爲true。

  如果同步操作30秒都沒有反應,則會重新選擇一個節點進行同步。

  到此,本章前面提到的問題全部解決了,不得不說MongoDB的設計還真是強大!

  後續繼續解決上一節這幾個問題:

  主節點掛了能否自動切換連接?目前需要手工切換。

  主節點的讀寫壓力過大如何解決?

  在系統早期,數據量還小的時候不會引起太大的問題,但是隨着數據量持續增多,後續遲早會出現一臺機器硬件瓶頸問題的。而MongoDB主打的就是海量數據架構,他不能解決海量數據怎麼行!“分片”就用這個來解決這個問題。

  傳統數據庫怎麼做海量數據讀寫?其實一句話概括:分而治之。上圖看看就清楚了,如下TaoBao嶽旭強提到的架構圖:

  

fenpian1

 

  上圖中有個TDDL,是TaoBao的一個數據訪問層組件,他主要的作用是SQL解析、路由處理。根據應用的請求的功能解析當前訪問的sql判斷是在哪個業務數據庫、哪個表訪問查詢並返回數據結果。具體如圖:

  

fenpian2

 

  說了這麼多傳統數據庫的架構,那NoSQL怎麼去做到了這些呢?MySQL要做到自動擴展需要加一個數據訪問層用程序去擴展,數據庫的增加、刪除、備份還需要程序去控制。一但數據庫的節點一多,要維護起來也是非常頭疼的。不過MongoDB所有的這一切通過他自己的內部機制就可以搞定!還是上圖看看MongoDB通過哪些機制實現路由、分片:

  

fenpian3

 

  從圖中可以看到有四個組件:mongos、config server、shard、replica set。

  mongos,數據庫集羣請求的入口,所有的請求都通過mongos進行協調,不需要在應用程序添加一個路由選擇器,mongos自己就是一個請求分發中心,它負責把對應的數據請求請求轉發到對應的shard服務器上。在生產環境通常有多mongos作爲請求的入口,防止其中一個掛掉所有的mongodb請求都沒有辦法操作。

  config server,顧名思義爲配置服務器,存儲所有數據庫元信息(路由、分片)的配置。mongos本身沒有物理存儲分片服務器和數據路由信息,只是緩存在內存裏,配置服務器則實際存儲這些數據。mongos第一次啓動或者關掉重啓就會從 config server 加載配置信息,以後如果配置服務器信息變化會通知到所有的 mongos 更新自己的狀態,這樣 mongos 就能繼續準確路由。在生產環境通常有多個 config server 配置服務器,因爲它存儲了分片路由的元數據,這個可不能丟失!就算掛掉其中一臺,只要還有存貨, mongodb集羣就不會掛掉。

  shard,這就是傳說中的分片了。上面提到一個機器就算能力再大也有天花板,就像軍隊打仗一樣,一個人再厲害喝血瓶也拼不過對方的一個師。俗話說三個臭皮匠頂個諸葛亮,這個時候團隊的力量就凸顯出來了。在互聯網也是這樣,一臺普通的機器做不了的多臺機器來做,如下圖:

  

fenpian4

 

  一臺機器的一個數據表 Collection1 存儲了 1T 數據,壓力太大了!在分給4個機器後,每個機器都是256G,則分攤了集中在一臺機器的壓力。也許有人問一臺機器硬盤加大一點不就可以了,爲什麼要分給四臺機器呢?不要光想到存儲空間,實際運行的數據庫還有硬盤的讀寫、網絡的IO、CPU和內存的瓶頸。在mongodb集羣只要設置好了分片規則,通過mongos操作數據庫就能自動把對應的數據操作請求轉發到對應的分片機器上。在生產環境中分片的片鍵可要好好設置,這個影響到了怎麼把數據均勻分到多個分片機器上,不要出現其中一臺機器分了1T,其他機器沒有分到的情況,這樣還不如不分片!

  replica set,上兩節已經詳細講過了這個東東,怎麼這裏又來湊熱鬧!其實上圖4個分片如果沒有 replica set 是個不完整架構,假設其中的一個分片掛掉那四分之一的數據就丟失了,所以在高可用性的分片架構還需要對於每一個分片構建 replica set 副本集保證分片的可靠性。生產環境通常是 2個副本 + 1個仲裁。

  說了這麼多,還是來實戰一下如何搭建高可用的mongodb集羣:

  首先確定各個組件的數量,mongos 3個, config server 3個,數據分3片 shard server 3個,每個shard 有一個副本一個仲裁也就是 3 * 2 = 6 個,總共需要部署15個實例。這些實例可以部署在獨立機器也可以部署在一臺機器,我們這裏測試資源有限,只准備了 3臺機器,在同一臺機器只要端口不同就可以,看一下物理部署圖:

  

fenpian5

 

  架構搭好了,安裝軟件!

  1. 準備機器,IP分別設置爲: 192.168.0.136、192.168.0.137、192.168.0.138。

  2. 分別在每臺機器上建立mongodb分片對應測試文件夾。

  #存放mongodb數據文件

  mkdir -p /data/mongodbtest

  #進入mongodb文件夾

  cd /data/mongodbtest

  3. 下載mongodb的安裝程序包

  wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz

  #解壓下載的壓縮包

  tar xvzf mongodb-linux-x86_64-2.4.8.tgz

  4. 分別在每臺機器建立mongos 、config 、 shard1 、shard2、shard3 五個目錄。

  因爲mongos不存儲數據,只需要建立日誌文件目錄即可。

  #建立mongos目錄

  mkdir -p /data/mongodbtest/mongos/log

  #建立config server 數據文件存放目錄

  mkdir -p /data/mongodbtest/config/data

  #建立config server 日誌文件存放目錄

  mkdir -p /data/mongodbtest/config/log

  #建立config server 日誌文件存放目錄

  mkdir -p /data/mongodbtest/mongos/log

  #建立shard1 數據文件存放目錄

  mkdir -p /data/mongodbtest/shard1/data

  #建立shard1 日誌文件存放目錄

  mkdir -p /data/mongodbtest/shard1/log

  #建立shard2 數據文件存放目錄

  mkdir -p /data/mongodbtest/shard2/data

  #建立shard2 日誌文件存放目錄

  mkdir -p /data/mongodbtest/shard2/log

  #建立shard3 數據文件存放目錄

  mkdir -p /data/mongodbtest/shard3/data

  #建立shard3 日誌文件存放目錄

  mkdir -p /data/mongodbtest/shard3/log

  5. 規劃5個組件對應的端口號,由於一個機器需要同時部署 mongos、config server 、shard1、shard2、shard3,所以需要用端口進行區分。

  這個端口可以自由定義,在本文 mongos爲 20000, config server 爲 21000, shard1爲 22001 , shard2爲22002, shard3爲22003.

  6. 在每一臺服務器分別啓動配置服務器。

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod --configsvr --dbpath /data/mongodbtest/config/data --port 21000 --logpath /data/mongodbtest/config/log/config.log --fork

  7. 在每一臺服務器分別啓動mongos服務器。

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongos --configdb 192.168.0.136:21000,192.168.0.137:21000,192.168.0.138:21000 --port 20000 --logpath /data/mongodbtest/mongos/log/mongos.log --fork

  8. 配置各個分片的副本集。

  #在每個機器裏分別設置分片1服務器及副本集shard1

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod --shardsvr --replSet shard1 --port 22001 --dbpath /data/mongodbtest/shard1/data --logpath /data/mongodbtest/shard1/log/shard1.log --fork --nojournal --oplogSize 10

  爲了快速啓動並節約測試環境存儲空間,這裏加上 nojournal 是爲了關閉日誌信息,在我們的測試環境不需要初始化這麼大的redo日誌。同樣設置 oplogsize是爲了降低 local 文件的大小,oplog是一個固定長度的 capped collection,它存在於”local”數據庫中,用於記錄Replica Sets操作日誌。注意,這裏的設置是爲了測試!

  #在每個機器裏分別設置分片2服務器及副本集shard2

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod --shardsvr --replSet shard2 --port 22002 --dbpath /data/mongodbtest/shard2/data --logpath /data/mongodbtest/shard2/log/shard2.log --fork --nojournal --oplogSize 10

  #在每個機器裏分別設置分片3服務器及副本集shard3

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod --shardsvr --replSet shard3 --port 22003 --dbpath /data/mongodbtest/shard3/data --logpath /data/mongodbtest/shard3/log/shard3.log --fork --nojournal --oplogSize 10

  分別對每個分片配置副本集,深入瞭解副本集參考本系列前幾篇文章。

  任意登陸一個機器,比如登陸192.168.0.136,連接MongoDB

  #設置第一個分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:22001

  #使用admin數據庫

  use admin

  #定義副本集配置

  config = { _id:"shard1", members:[

  {_id:0,host:"192.168.0.136:22001"},

  {_id:1,host:"192.168.0.137:22001"},

  {_id:2,host:"192.168.0.138:22001",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  #設置第二個分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:22002

  #使用admin數據庫

  use admin

  #定義副本集配置

  config = { _id:"shard2", members:[

  {_id:0,host:"192.168.0.136:22002"},

  {_id:1,host:"192.168.0.137:22002"},

  {_id:2,host:"192.168.0.138:22002",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  #設置第三個分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:22003

  #使用admin數據庫

  use admin

  #定義副本集配置

  config = { _id:"shard3", members:[

  {_id:0,host:"192.168.0.136:22003"},

  {_id:1,host:"192.168.0.137:22003"},

  {_id:2,host:"192.168.0.138:22003",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  9. 目前搭建了mongodb配置服務器、路由服務器,各個分片服務器,不過應用程序連接到 mongos 路由服務器並不能使用分片機制,還需要在程序裏設置分片配置,讓分片生效。

  #連接到mongos /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:20000

  #使用admin數據庫 user admin

  #串聯路由服務器與分配副本集1

  db.runCommand( { addshard : "shard1/192.168.0.136:22001,192.168.0.137:22001,192.168.0.138:22001"});

  如裏shard是單臺服務器,用 db.runCommand( { addshard : “ [: ]” } )這樣的命令加入,如果shard是副本集,用db.runCommand( { addshard : “replicaSetName/ [:port][,serverhostname2[:port],…]” });這樣的格式表示 。

  #串聯路由服務器與分配副本集2

  db.runCommand( { addshard : "shard2/192.168.0.136:22002,192.168.0.137:22002,192.168.0.138:22002"});

  #串聯路由服務器與分配副本集3

  db.runCommand( { addshard : "shard3/192.168.0.136:22003,192.168.0.137:22003,192.168.0.138:22003"});

  #查看分片服務器的配置

  db.runCommand( { listshards : 1 } );

  #內容輸出

  [plain] view plaincopy{

  "shards" : [

  {

  "_id" : "shard1",

  "host" : "shard1/192.168.0.136:22001,192.168.0.137:22001"

  },

  {

  "_id" : "shard2",

  "host" : "shard2/192.168.0.136:22002,192.168.0.137:22002"

  },

  {

  "_id" : "shard3",

  "host" : "shard3/192.168.0.136:22003,192.168.0.137:22003"

  }

  ],

  "ok" : 1

  }

  因爲192.168.0.138是每個分片副本集的仲裁節點,所以在上面結果沒有列出來。

  10. 目前配置服務、路由服務、分片服務、副本集服務都已經串聯起來了,但我們的目的是希望插入數據,數據能夠自動分片,就差那麼一點點,一點點。。。

  連接在mongos上,準備讓指定的數據庫、指定的集合分片生效。

  #指定testdb分片生效

  db.runCommand( { enablesharding :"testdb"});

  #指定數據庫裏需要分片的集合和片鍵

  db.runCommand( { shardcollection : "testdb.table1",key : {id: 1} } )

  我們設置testdb的 table1 表需要分片,根據 id 自動分片到 shard1 ,shard2,shard3 上面去。要這樣設置是因爲不是所有mongodb 的數據庫和表 都需要分片!

  11. 測試分片配置結果。

  #連接mongos服務器

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:20000

  #使用testdb use testdb;

  #插入測試數據

  for (var i = 1; i <= 100000; i++)

  db.table1.save({id:i,"test1":"testval1"});

  #查看分片情況如下,部分無關信息省掉了

  db.table1.stats();

  [java] view plaincopy{

  "sharded" : true,

  "ns" : "testdb.table1",

  "count" : 100000,

  "numExtents" : 13,

  "size" : 5600000,

  "storageSize" : 22372352,

  "totalIndexSize" : 6213760,

  "indexSizes" : {

  "_id_" : 3335808,

  "id_1" : 2877952

  },

  "avgObjSize" : 56,

  "nindexes" : 2,

  "nchunks" : 3,

  "shards" : {

  "shard1" : {

  "ns" : "testdb.table1",

  "count" : 42183,

  "size" : 0,

  ...

  "ok" : 1

  },

  "shard2" : {

  "ns" : "testdb.table1",

  "count" : 38937,

  "size" : 2180472,

  ...

  "ok" : 1

  },

  "shard3" : {

  "ns" : "testdb.table1",

  "count" :18880,

  "size" : 3419528,

  ...

  "ok" : 1

  }

  },

  "ok" : 1

  }

  可以看到數據分到3個分片,各自分片數量爲: shard1 “count” : 42183,shard2 “count”: 38937,shard3 “count” : 18880。已經成功了!不過分的好像不是很均勻,所以這個分片還是很有講究的,後續再深入討論。

  12. Java程序調用分片集羣,因爲我們配置了三個mongos作爲入口,就算其中哪個入口掛掉了都沒關係,使用集羣客戶端程序如下:[java] view plaincopypublic class TestMongoDBShards { public static void main(String[] args)

  { try { List addresses = new ArrayList();

  ServerAddress address1 = new ServerAddress("192.168.0.136" , 20000); ServerAddress

  address2 = new ServerAddress("192.168.0.137" , 20000); ServerAddress address3

  = new ServerAddress("192.168.0.138" , 20000); addresses.add(address1);

  addresses.add(address2); addresses.add(address3); MongoClient client =

  new MongoClient(addresses); DB db = client.getDB( "testdb" ); DBCollection

  coll = db.getCollection( "table1" ); BasicDBObject object = new BasicDBObject();

  object.append( "id" , 1); DBObject dbObject = coll.findOne(object); System.

  out .println(dbObject); } catch (Exception e) { e.printStackTrace(); }

  } }

  整個分片集羣搭建完了,思考一下我們這個架構是不是足夠好呢?其實還有很多地方需要優化,比如我們把所有的仲裁節點放在一臺機器,其餘兩臺機器承擔了全部讀寫操作,但是作爲仲裁的192.168.0.138相當空閒。讓機器3 192.168.0.138多分擔點責任吧!架構可以這樣調整,把機器的負載分的更加均衡一點,每個機器既可以作爲主節點、副本節點、仲裁節點,這樣壓力就會均衡很多了,如圖:

  

fenpian6

 

  當然生產環境的數據遠遠大於當前的測試數據,大規模數據應用情況下我們不可能把全部的節點像這樣部署,硬件瓶頸是硬傷,只能擴展機器。要用好mongodb還有很多機制需要調整,不過通過這個東東我們可以快速實現高可用性、高擴展性,所以它還是一個非常不錯的Nosql組件。

  再看看我們使用的mongodb java 驅動客戶端 MongoClient(addresses),這個可以傳入多個mongos 的地址作爲mongodb集羣的入口,並且可以實現自動故障轉移,但是負載均衡做的好不好呢?打開源代碼查看:

  

fenpian7

 

   它的機制是選擇一個ping 最快的機器來作爲所有請求的入口,如果這臺機器掛掉會使用下一臺機器。那這樣。。。。肯定是不行的!萬一出現雙十一這樣的情況所有請求集中發送到這一臺機器,這臺機器很有可能掛掉。一但掛掉了,按照它的機制會轉移請求到下臺機器,但是這個壓力總量還是沒有減少啊!下一臺還是可能崩潰,所以這個架構還有漏洞!限於文章篇幅,請待後續解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章