MongoDB 分散式部署教程
本文將介紹如何使用 MongoDB 提供的 Replica Set 和 Shards 功能構建一個分散式 MongoDB 叢集。
Replica Set 部署
我們先從部署一個三節點的 Replica Set 開始。
首先,我們要為每個 mongod
例項建立它自己的 dbpath
:
1 |
mkdir 1 |
然後,我們便可以開始啟動這三個 mongod
例項了:
1 |
mongod --dbpath 1 --port 27001 --replSet myRS |
注意,這裡我是為了在同一臺機器上執行三個 mongod
例項,所以需要為它們分別指定不同的埠。如果是真實的分散式 Replica Set,在每臺機器上使用預設的 27017
埠是完全可行的。
除此之外,我使用 --replSet
引數指定了 mongod
例項所屬 Replica Set 的名字。這個名字是可以隨意起的,但必須確保屬於同一個 Replica Set 的 mongod
例項設定了相同的 --replSet
,否則可能會產生一些不可預期的後果。
在順利開啟這些 mongod
1 |
2015-11-14T16:25:46.060+0800 I JOURNAL [initandlisten] journal dir=3\journal |
可以注意到,倒數第二條記錄顯示 mongod
未能在本地資料中找到 Replica Set 的設定資訊。這是正常的,因為這是第一次建立的 Replica Set。最後一條資訊顯示 mongod
啟動完畢,等待外界連線它的埠。
那麼,我們開始啟動 Replica Set。使用 mongo
連入隨便一個 mongod
例項,並進行設定:
1 |
var conf = { |
在 conf
中,我們將 _id
設定為 Replica Set 的名稱,並在 members
中設定了 Replica Set 所有成員的資訊,其中包括成員的名稱 _id
以及成員的主機名 host
。
注意,儘管這裡可以直接使用了
IP:埠
的形式來指定mongod
例項,但在真實環境中,不要這麼做,這種做法十分糟糕。不過現在搭建分散式,大家的做法似乎更傾向於為每臺機器修改hosts
檔案。同樣,不要這麼做,這兩種做法都屬於 bad practice。最好的做法,是在你的叢集環境中配置一臺 DNS 伺服器。這樣,當你的某一個結點的 IP 發生變化時,你就只需要修改 DNS 伺服器中的那條解析條目,而不需要修改每個結點的hosts
檔案了。
直接以數字作為每個結點的名稱也是不好的做法,因為這個名稱在
mongod
的日誌資訊中會經常出現。使用更加可讀的名稱是更好的做法。
一切正常的話,你應該會在其中一個結點上看到如下日誌資訊:
1 |
2015-11-14T16:41:54.946+0800 I NETWORK [initandlisten] connection accepted from 127.0.0.1:61875 #1 (1 connection now open) |
從日誌中,我們可以很清晰地看到,發起 rs.initiate
的 mongod
向其他 mongod
開啟了連線,其他 mongod
獲取到了我們配置的 conf
資訊。而後,Replica Set 開始啟動。首先是各結點進行初始化同步,從發起 rs.initiate
的 mongod
處同步了 oplog,並進入 Secondary
狀態。然後,3 個 Secondary
發現 Replica Set 中沒有 Primary
,於是發起選舉。日誌裡,我們甚至可以看到這個 mongod
把票投給了誰。最後,選舉結束,localhost:27001
成為了 Primary
。
使用 Java 驅動連線至 Replica Set
我們通過如下語句連線至單一的 MongoDB 例項:
1 |
MongoClient client = new MongoClient("localhost", 27001); |
我們為 MongoClient
物件指定了一個 MongoDB 例項的主機名和埠號。以這種方式初始化的 MongoClient
會假設目標 MongoDB 例項只是一個 standalone 的例項,如果該例項不是 Primary
時,客戶端執行寫操作則可能被該 MongoDB 例項拒絕。
通過如下語句可使 MongoClient
進入 Replica Set 模式:
1 |
MongoClient client = new MongoClient(asList( |
我們通過 Arrays#asList
方法為 MongoClient
傳入了一個 List
,MongoClient
便會進入 Replica Set 模式。在這種模式下,客戶端會利用給定的主機(seedlist)來發現 Replica Set 的其他所有結點,其中就包括了 Primary
。因此,即使 localhost:27001
不是 Primary
也沒關係,客戶端會通過它獲知 Primary
的地址並自動連線至 Primary
。
但以上做法仍不全面:如果 localhost:27001
程序已經掛了,或者它並不是 Replica Set 的成員,我們便無法通過上述語句連線至 Replica Set。 我們可以為建構函式傳入更多的 MongoDB 例項的地址來降低這種情況發生的機率:
1 |
MongoClient client = new MongoClient(asList( |
當然,也有可能正好你指定的這多個結點都同時掛掉,那樣自然是防不勝防了。不過,提高 Replica Set 拓撲可用性就是網路架構的問題了。當我們在執行寫操作時,我們還需要考慮 Primary
會突然掛掉。比如說,我們正在執行這樣的寫操作:
1 |
MongoCollection collection = client.getDatabase("foo").getCollection("bar"); |
在執行插入時,如果 Primary
突然失效(如呼叫了 rs.stepDown()
),那麼上述程式碼中的 insertOne
方法會丟擲一個錯誤。因此,更為健壯的做法,是為該 insertOne
語句加上 try/catch
塊:
1 |
for (int i = 0; i < Integer.MAX_VALUE; i++) { |
遺憾的是,丟擲錯誤的 insertOne
操作恐怕無法由 MongoDB 驅動自動重試。實際上,不只是觸發錯誤的那一次操作,在 Replica Set 自動選舉出新的 Primary
前,所有寫操作都會丟擲錯誤。但幸運的是,由於加上了 try/catch
塊,應用程式不會因為單次寫入失敗便直接退出。在觸發錯誤後,下一次插入前驅動都會重新嘗試利用 seedlist 來獲取新的 Primary
的地址。當 Replica Set 重新選舉出新的 Primary
後,驅動便可以再次進行寫操作了。
通過觀察 MongoDB Java 驅動輸出的日誌資訊,你可以更細緻地觀察驅動的行為。這裡就不直接給出了,有興趣可自己嘗試。
Shard 叢集部署
在本節中,我們將會在本機上部署一個完整的生產級別的 MongoDB Shard 叢集。叢集由 4 個 Shard 負責儲存資料,其中每個 Shard 都是包含三個結點的 Replica Set。除此之外,叢集還包括 4 個 mongos
和 3 個 Config Server。
注意,用於生產環境的 Shard 叢集必須遵循如下幾個原則:必須使用 Replica Set 來作為 Shard,任何一個 Shard 的不可用都會導致叢集出現異常;必須使用正好 3 個 Config Server,Config Server 不可用將導致整個叢集不可用。除此之外,使用兩個以上的
mongos
例項可以更好地分散壓力。
4 個 Replica Set 的資訊分別如下:
1 |
{ |
叢集各成員啟動
首先我們分別啟動叢集的各個成員,分別是 Shard、Config Server 和 Query Router。其中前兩種成員均為 mongod
,而 Query Router 則是 mongos
。
單個 Replica Set 的配置方式大致上無太大變化,只是作為 Shard Server 在啟動 mongod
時需要加上–shardsvr選項。 以 Replica Set a
為例:
1 |
mkdir a{1,2,3} |
注意:當
--shardsvr
選項被開啟時,mongod
的預設埠號變為 27018。
再使用 mongo
連線至任意一個 mongod
例項,啟動 Replica Set:
1 |
var conf = { |
重複上述操作即可啟動其餘三個 Replica Set。
接下來開始啟動 Config Server:
1 |
mkdir cfg{1,2,3} |
注意:當
--configvr
選項被開啟時,mongod
的預設埠號變為27019。
最後,啟動 Query Router:
1 |
mongos --configdb localhost:26050,localhost:26051,localhost:26052 --logpath log.mongos1 --fork |
注意:
mongos
的預設埠號為27017,與mongod
、mongo
的預設埠號相同。
如此一來,叢集的各個成員都啟動完畢了,可以開始配置叢集了。
新增 Shard
實際上,在啟動 mongos
時,我們已經指定了叢集所使用的 Config Server 的地址。接下來就是為叢集指定每個 Shard 的地址了。
開啟 mongo
連線至任意一個 mongos
,並執行如下指令:
1 |
sh.addShard("a/localhost:27001") |
注意到,我們新增 Shard 時,輸入了 Replica Set 的名稱以及其中一個成員的地址。該成員並不一定得是 Primary
,只要它是該 Replica Set 的成員,mongos
就能自動發現 Replica Set 的其他所有成員。
在添加了 4 個 Shard 以後,整個 Shard 叢集便配置完畢,可以開始使用了。
from: https://mr-dai.github.io/mongodb_distribution_tutorial/