mongodb 分片

分片邏輯圖

上節搭建的分片集羣從邏輯上看以下圖所示:web

 

:能夠普通的mongod進程,也能夠是副本集。可是即便一片內有多臺服務器,也只能有一個主服務器,其餘的服務器保存相同的數據。算法

mongos路由進程:它路由全部請求,而後將結果聚合。它不保存存儲數據或配置信息。mongodb

配置服務器:存儲集羣的配置信息。shell

整個分佈式的集羣經過mongos對客戶端提供了一個透明統一的接口,客戶端不須要關係具體的分片細節,全部分片的動做都是自動執行的,那是如何作到透明和自動的。數據庫

切分數據

上節中創建好集羣后,默認的是不會將存儲的每條數據進行分片處理,須要在數據庫和集合的粒度上都開啓分片功能。開啓test庫的分片功能:服務器

.    ./bin/mongo –port .    mongos>.    mongos> db.runCommand({:.    {  :

開啓user集合分片功能:架構

.    mongos> db.runCommand({:,:{:.    {  : ,  :  }

注意:須要切換到admin庫執行命令。socket

片鍵:上面的key就是所謂的片鍵(shard key)。MongoDB不容許插入沒有片鍵的文檔。可是容許不一樣文檔的片鍵類型不同,MongoDB內部對不一樣類型有一個排序:分佈式

片鍵的選擇相當重要,後面會進行詳細的說明。工具

這時再切換到config庫以下查看:

複製代碼

.    mongos>.    mongos>.    {  : ,  : ,  : .    {  : ,  : ,  : .    {  : ,  : ,  : .    {  : ,  : ,  : .    {  : ,  : ,  : .    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  : {  :  } },  :  }

複製代碼

Chunks:理解MongoDB分片機制的關鍵是理解Chunks。mongodb不是一個分片上存儲一個區間,而是每一個分片包含多個區間,這每一個區間就是一個塊。

.    mongos>.    mongos>.    {  : ,  : .    ……

平衡:若是存在多個可用的分片,只要塊的數量足夠多,MongoDB就會把數據遷移到其餘分片上,這個遷移的過程叫作平衡。Chunks默認的大小是64M200M,查看config.settings能夠看到這個值:

只有當一個塊的大小超過了64M200M,MongoDB纔會對塊進行分割(但根據個人實踐2.4版本塊分割的算法好像並非等到超過chunkSize大小就分割,也不是一分爲二,有待繼續學習),並當最大分片上的塊數量超過另外一個最少分片上塊數量達到必定閾值會發生所謂的chunk migration來實現各個分片的平衡 (若是啓動了balancer進程的話)。這個閾值隨着塊的數量不一樣而不一樣:

 

這帶來一個問題是咱們測開發測試的時候,每每但願經過觀察數據在遷移來證實分片是否成功,但64M200M顯然過大,解決方法是咱們能夠在啓動mongos的時候用—chunkSize來制定塊的大小,單位是MB。

.    ./bin/mongos --port  --configdb .: --logpath log/mongos.log  --fork --chunkSize

我指定1MB的塊大小從新啓動了一下mongos進程。

或者向下面這要修改chunkSize大小:

.    mongos>.    mongos> db.settings.save( { _id:, value:  } )

 2.4版本以前MongoDB基於範圍進行分片的(2.2.4會介紹基於哈希的分片),對一個集合分片時,一開始只會建立一個塊,這個塊的區間是(-∞,+∞),-∞表示MongoDB中的最小值,也就是上面db.chunks.find()咱們看到的$minKey,+∞表示最大值即$maxKey。Chunck的分割是自動執行的,相似於細胞分裂,從區間的中間分割成兩個。

分片測試

 如今對上面咱們搭建的MongoDB分片集羣作一個簡單的測試。目前咱們的集羣狀況以下(爲了方面展現我採用了可視化的工具MongoVUE):

像上面提到那樣爲了儘快能觀察到測試的效果,我麼啓動mongos時指定的chunkSize爲1MB。

咱們對OSSP10庫和bizuser集合以Uid字段進行分片:

.    mongos>.    mongos> db.runCommand({:.    {  : .    mongos> db.runCommand({:,:{:.    {  : ,  :  }

在插入數據以前查看一下config.chunks,經過查看這個集合咱們能夠了解數據是怎麼切分到集羣的:

.    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  : {  :  } },  :  }

開始只有一個塊,區間(-∞,+∞)在shard0000(192.168.32.13:27019)上,下面咱們循環的插入100000條數據:

.    mongos>.    mongos> (i=;i<;i++){ db.bizuser.insert({:i,:,:,: Date()}); }

完成後咱們在觀察一下config.chunks的狀況:

複製代碼

.    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  : {  :  } },  :  }

複製代碼

咱們能夠看見剛開始的塊分裂成了三塊分別是(-∞,0)在shard0002上,[0,6747)在shard0000上和[6747,+ ∞)在shard0001上。

說明:這裏須要說明的是MongoDB中的區間是左閉右開的。這樣說上面第一個塊不包含任何數據,至於爲何還不清楚,有待繼續調研。

咱們持續上面的插入操做(uid從0到100000)咱們發現塊也在不停的分裂:

複製代碼

.    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  : {  :  } },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  :  }

複製代碼

分片的規則正是上面提到的塊的自動分裂和平衡,能夠發現不一樣的塊是分佈在不一樣的分片上。

注意:這裏使用Uid做爲片鍵一般是有問題的,2.3對遞增片鍵有詳細說明。

Note:經過sh.status()能夠很直觀的查看當前整個集羣的分片狀況,相似以下:

對已有數據進行分片

面的演示都是從零開始構建分片集羣,可是實際中架構是一個演進的過程,一開始都不會進行分片,只有當數據量增加到必定程序纔會考慮分片,那麼對已有的海量數據如何處理。咱們在上節的基礎上繼續探索。《深刻學習MongoDB》中有以下描述:

咱們如今來嘗試下(也許我使用的最新版本會有全部變化),先在192.168.71.43:27179上再啓一個mongod進程:

.    numactl --interleave=all ./bin/mongod --dbpath data/shard3/ --logpath log/shard3.log  --fork --port

如今咱們像這個mongod進程中的OSSP10庫的bizuser集合中插入一些數據:

.    (i=;i<;i++){ db.bizuser.insert({:i,:,:,: Date()}); }

咱們這個時候嘗試將這個mongod加入到以前的集羣中去:

.    mongos> db.runCommand({addshard:.             : .             : .    }

果真最新版本依舊不行。咱們刪除OSSP10庫,新建個OSSP20庫再添加分片則能夠成功。

.    {  : ,  :  }

那麼看來咱們只能在以前已有的數據的mongod進程基礎上搭建分片集羣,那麼這個時候添加切片,MongoDB對以前的數據又是如何處理的呢?咱們如今集羣中移除上面添加的切片192.168.71.43:27019,而後在刪除集羣中的OSSP10庫。

此次咱們儘可能模擬生產環境進行測試,從新啓動mongos並不指定chunksize,使用默認的最優的大小(64M200M)。而後在192.168.71.43:27019中新建OSSP10庫並向集合bizuser中插入100W條數據:

.    (i=;i<;i++){ db.bizuser.insert({:i,:,:,: Date()}); }

而後將此節點添加到集羣中,並仍然使用遞增的Uid做爲片鍵對OSSP10.bizuser進行分片:

.    mongos>.    mongos> db.runCommand({:.    {  : .    mongos> db.runCommand({:,:{:.    {  : ,  :  }

觀察一下config.chunks能夠看見對新添加的切片數據進行切分並進行了平衡(每一個分片上一個塊),基本是對Uid(0~1000000)進行了四等分,固然沒那精確:

複製代碼

.    mongos>.    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  : {  :  } },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  :  },  : {  :  },  :  }

複製代碼

MongoDB這種自動分片和平衡的能力使得在遷移老數據的時候變得很是簡單,可是若是數據特別大話則可能會很是的慢。

Hashed Sharding

MongoDB2.4以上的版本支持基於哈希的分片,咱們在上面的基礎上繼續探索。

交代一下由於環境變更這部分示例操做不是在以前的那個集羣上,可是系統環境都是同樣,邏輯架構也同樣,只是ip地址不同(只搭建在一臺虛擬機上),後面環境有改動再也不說明:

配置服務器:192.168.129.132:10000

路由服務器:192.168.129.132:20000

分片1:192.168.129.132:27017

分片2:192.168.129.132:27018

……其餘分片端口依次遞增。

咱們重建以前的OSSP10庫,咱們仍然使用OSSP10.bizuser不過此次啓動哈希分片,選擇_id做爲片鍵:

.    mongos>.    mongos> db.runCommand({:.    {  : .    mongos> db.runCommand({:,:{:.    {  : ,  :  }

咱們如今查看一下config.chunks

複製代碼

.    mongos>.    mongos>.    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : {  :  } },  : {  : NumberLong() },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : NumberLong() },  : {  : NumberLong() },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : NumberLong() },  : {  : NumberLong() },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : NumberLong() },  : {  : {  :  } },  :  }

複製代碼

MongoDB的哈希分片使用了哈希索引:

哈希分片仍然是基於範圍的,只是將提供的片鍵散列成一個很是大的長整型做爲最終的片鍵。官方文中描述以下:

不像普通的基於範圍的分片,哈希分片的片鍵只能使用一個字段。

 

選擇哈希片鍵最大的好處就是保證數據在各個節點分佈基本均勻,下面使用_id做爲哈希片鍵作個簡單的測試:

.    mongos> db.runCommand({:.    db.runCommand({:,:{:.    mongos>.    mongos> (i=;i<;i++){ db.mycollection.insert({:i,:,:,:new Date()}); }

 

經過MongoVUE觀察三個切片上的數據量很是均勻:

上面是使用官方文檔中推薦的Objectid做爲片鍵,狀況很理想。若是使用一個自增加的Uid做爲片鍵呢:

.    db.runCommand({:,:{:.    (i=;i<;i++){ db.mycollection.insert({:i,:,:,:new Date()}); }

故障恢復

先不考慮集羣中每一個分片是副本集複雜的狀況,只考慮了每一個分片只有一個mongod進程,這種配是隻是不夠健壯仍是很是脆弱。咱們在testdb.testcollection上啓動分片,而後向其中插入必定量的數據(視你的chunkSize而定),經過觀察config.chunks確保testdb.testcollection上的數據被分佈在了不一樣的分片上。

複製代碼

.    mongos> db.runCommand({:.    mongos> db.runCommand({:,:{:.    (i=;i<;i++){ db.testcollection.insert({:i,:,:,.    mongos>.    mongos> db.chunks..    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : },  : {  : },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : },  : {  : },  : .    {  : ,  : {  : ,  :  },  : ObjectId(),  : ,  : {  : },  : {  : },  :  }

複製代碼

這時咱們強制殺掉shard2進程:

.    [root@localhost mongodb-.]#  -ef |.    root              : ?        :: ./bin/mongod --dbpath data/shard3/ --logpath log/shard3.log --fork --port .     -

咱們嘗試用屬於不一樣範圍的Uid對testdb.testcollection進行寫入操做(這裏插入一條記錄應該不會致使新的塊遷移):

複製代碼

.    [root@localhost mongodb-.]#  -ef |.    mongos> db.testcollection.insert({:,:,:,.    mongos> db.testcollection.insert({:,:,:,.    mongos> db.testcollection.insert({:,:,:,.    socket exception [CONNECT_ERROR]  .:.    mongos> db.testcollection.({Uid:.    mongos> db.testcollection.({Uid: .    mongos> db.testcollection.({Uid: .         : .         : .         : .    Sat Apr   ::.         : .         : .         : .    } at src/mongo/shell/query.js:L180

複製代碼

能夠看到插入操做涉及到分片shard0002的操做都沒法完成。這是瓜熟蒂落的。

下面咱們從新啓動shard3看集羣是否能自動恢復正常操做:

複製代碼

.    ./bin/mongod --dbpath data/shard3/ --logpath log/shard3.log --fork --port .    mongos>.    mongos> db.shards..    {  : ,  : .    {  : ,  : .    {  : ,  :  }

複製代碼

重複上面的插入和讀取shard0002的操做:

.    mongos> db.testcollection.insert({:,:,:,.    db.testcollection.({Uid: .    {  : ObjectId(),  : ,  : ,  : ,  : ISODate(.    ##沒有問題

總結:當集羣中某個分片宕掉之後,只要不涉及到該節點的操縱仍然能進行。當宕掉的節點重啓後,集羣能自動從故障中恢復過來。

這一節在前一節搭建的集羣基礎上作了一些簡單的測試,下一節的重點是性能和優化相關。

相關文章
相關標籤/搜索