標籤:record out value star 導致 last 資料集 結構 port
在MongoDB(版本 3.2.9)中,分區叢集(sharded cluster)是一種水平擴充資料庫系統效能的方法,能夠將資料集分布式儲存在不同的分區(shard)上,每個分區只儲存資料集的一部分,MongoDB保證各個分區之間不會有重複的資料,所有分區儲存的資料之和就是完整的資料集。分區叢集將資料集分布式儲存,能夠將負載分攤到多個分區上,每個分區只負責讀寫一部分資料,充分利用了各個shard的系統資源,提高資料庫系統的輸送量。
資料集被拆分成資料區塊(chunk),每個資料區塊包含多個doc,資料區塊分布式儲存在分區叢集中。MongoDB負責追蹤資料區塊在shard上的分布資訊,每個分區儲存哪些資料區塊,叫做分區的中繼資料,儲存在config server上的資料庫 config中,一般使用3台config server,所有config server中的config資料庫必須完全相同。通過mongos能夠直接存取資料庫config,查看分區的中繼資料;mongo shell 提供 sh 輔助函數,能夠安全地查看分區叢集的中繼資料資訊。
對任何一個shard進行查詢,只會擷取collection在當前分區上的資料子集,不是整個資料集。Application 只需要串連到mongos,對其進行的讀寫操作,mongos自動將讀寫請求路由到相應的shard。MongoDB通過mongos將分區的底層實現對Application透明,在Application看來,訪問的是整個資料集。
一,主分區
在分區叢集中,不是每個集合都會分布式儲存,只有使用sh.shardCollection()顯式將collection分區後,該集合才會分布式儲存在不同的shard中。對於非分區集合(un-sharded collection),其資料只會儲存在主分區(Primary shard)中,預設情況下,主分區是指資料庫最初建立的shard,用於儲存該資料庫中非分區集合的資料。每個資料庫都有一個主分區。
Each database in a sharded cluster has a primary shard that holds all the un-sharded collections for that database. Each database has its own primary shard.
例如,一個分區叢集有三個分區:shard1,shard2,shard3,在分區shard1建立一個資料庫blog。如果將資料庫bolg分區,那麼MongoDB會自動在shard2,shard3上建立一個結構相同的資料庫blog,資料庫blog的Primary Shard是Shard1。
圖示,Collection2的主分區是ShardA。
使用 movePrimary命令變更資料庫預設的Primary shard,非分區集合將會從當前shard移動到新的主分區。
db.runCommand( { movePrimary : "test", to : "shard0001" } )
在使用movePrimary命令變更資料庫的主分區之後,config server中的配置資訊是最新的,mongos緩衝的配置資訊變得過時了。MongoDB提供命令:flushRouterConfig 強制mongos從config server擷取最新的配置資訊,重新整理mongos的緩衝。
db.adminCommand({"flushRouterConfig":1})
二,分區的中繼資料
不要直接到config server上查看分區叢集的中繼資料資訊,這些資料非常重要,安全的方式是通過mongos串連到config資料查看,或者使用sh輔助函數查看。
使用sh輔助函數查看
sh.status()
串連到mongos查看config資料庫中的集合
mongos> use config
1,shards 集合儲存分區資訊
db.shards.find()
shard的資料存放區在host指定的 replica set 或 standalone mongod中。
{ "_id" : "shard_name", "host" : "replica_set_name/host:port", "tag":[shard_tag1,shard_tag2] }
2,databases集合儲存分區叢集中所有資料庫的資訊,不管資料庫是否分區
db.databases.find()
如果在資料庫上執行sh.enableSharding("db_name"),那麼欄位partitioned欄位值就是true;primary 欄位指定資料庫的主分區(primary shard)。
{ "_id" : "test", "primary" : "rs0", "partitioned" : true}
3,collections集合儲存所有已分區集合的資訊,不包括非分區集合(un-sharded collections)
key是:分區的片鍵
db.collections.find(){ "_id" : "test.foo", "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"), "lastmod" : ISODate("1970-02-19T17:02:47.296Z"), "dropped" : false, "key" : { "_id" : 1 }, "unique" : true}
4,chunks 集合儲存資料區塊資訊,
ns:分區的集合,結構是:db_name.collection_name
min 和 max: 片鍵的最小值和最大值
shard:塊所在的分區
db.chunks.find(){ "_id" : "test.foo-_id_MinKey", "lastmod" : Timestamp(1, 1), "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"), "ns" : "test.foo", "min" : { "_id" : 1 }, "max" : { "_id" : 3087 }, "shard" : "rs0"}
5,changelog集合記錄分區叢集的操作,包括chunk的拆分和遷移操作,Shard的增加或刪除操作
what 欄位:表示操作的類型,例如:multi-split表示chunk的拆分,
"what" : "addShard","what" : "shardCollection.start","what" : "shardCollection.end", "what" : "multi-split",
6,tags 記錄shard的tag和對應的片鍵範圍
{ "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001" } }, "ns" : "records.users", "min" : { "zipcode" : "10001" }, "max" : { "zipcode" : "10281" }, "tag" : "NYC"}
7,settings 集合記錄均衡器狀態和chunk的大小,預設的chunk size是64MB。
{ "_id" : "chunksize", "value" : 64 }
{ "_id" : "balancer", "stopped" : false }
8,locks 集合記錄分布鎖(distributed lock),保證只有一個mongos 執行個體能夠在分區叢集中執行管理工作。
mongos在擔任balancer時,會擷取一個分布鎖,並向config.locks中插入一條doc。
The locks collection stores a distributed lock. This ensures that only one mongos instance can perform administrative tasks on the cluster at once. The mongos acting as balancer takes a lock by inserting a document resembling the following into the locks collection.
{ "_id" : "balancer", "process" : "example.net:40000:1350402818:16807", "state" : 2, "ts" : ObjectId("507daeedf40e1879df62e5f3"), "when" : ISODate("2012-10-16T19:01:01.593Z"), "who" : "example.net:40000:1350402818:16807:Balancer:282475249", "why" : "doing balance round"}
三,刪除分區
刪除分區時,必須確保該分區上的資料被移動到其他分區中,對於以分區的集合,使用均衡器來遷移資料區塊,對於非分區的集合,必須修改集合的主分區。
1,刪除已分區的集合資料
step1,保證均衡器是開啟的
sh.setBalancerState(true);
step2,將已分區的集合全部遷移到其他分區
use admin
db.adminCommand({"removeShard":"shard_name"})
removeShard命令會將資料區塊從當前分區上遷移到其他分區上去,如果分區上的資料區塊比較多,遷移過程可能耗時很長。
step3,檢查資料區塊遷移的狀態
use admindb.runCommand( { removeShard: "shard_name" } )
使用removeShard命令能夠查看資料區塊遷移的狀態,remaining 欄位表示剩餘資料區塊的數量
{ "msg" : "draining ongoing", "state" : "ongoing", "remaining" : { "chunks" : 42, "dbs" : 1 }, "ok" : 1}
step4,資料區塊完成遷移
use admindb.runCommand( { removeShard: "shard_name" } ){ "msg" : "removeshard completed successfully", "state" : "completed", "shard" : "shard_name", "ok" : 1}
2,刪除未分區的資料庫
step1,查看未分區的資料庫
未分區的資料庫,包括兩部分:
- 資料庫未被分區,該資料沒有使用sh.enableSharding("db_name"),在資料庫config中,該資料庫的partitioned欄位是false
- 資料庫中存在collection未被分區,即當前的分區是該集合的主分區
use configdb.databases.find({$or:[{"partitioned":false},{"primary":"shard_name"}]})
對於partitioned=false的資料庫,其資料全部儲存在當前shard中;對於partitioned=true,primary=”shard_name“的資料庫,表示存在未分區(un-sharded collection)儲存在該資料庫中,必須變更這些集合的主分區。
step2,修改資料庫的主分區
db.runCommand( { movePrimary: "db_name", to: "new_shard" })
四,增加分區
由於分區儲存的是資料集的一部分,為了保證資料的高可用性,推薦使用Replica Set作為shard,即使Replica Set中只包含一個成員。串連到mongos,使用sh輔助函數增加分區。
sh.addShard("replica_set_name/host:port")
不推薦將standalone mongod作為shard
sh.addShard("host:port")
五,特大塊
在有些情況下,chunk會持續增長,超出chunk size的限制,成為特大塊(jumbo chunk),出現特大塊的原因是chunk中的所有doc使用同一個片鍵(shard key),導致MongoDB無法拆分該chunk,如果該chunk持續增長,將會導致chunk的分布不均勻,成為效能瓶頸。
在chunk遷移時,存在限制:每個chunk的大小不能超過2.5萬條doc,或者1.3倍於配置值。chunk size預設的配置值是64MB,超過限制的chunk會被MongoDB標記為特大塊(jumbo chunk),MongoDB不能將特大塊遷移到其他shard上。
MongoDB cannot move a chunk if the number of documents in the chunk exceeds either 250000 documents or 1.3 times the result of dividing the configured chunk size by the average document size.
1,查看特大塊
使用sh.status(),能夠發現特大塊,特大塊的後面存在 jumbo 標誌
{ "x" : 2 } -->> { "x" : 3 } on : shard-a Timestamp(2, 2) jumbo
2,分發特大塊
特大塊不能拆分,不能通過均衡器自動分發,必須手動分發。
step1,關閉均衡器
sh.setBalancerState(false)
step2,增大Chunk Size的配置值
由於MongoDB不允許移動大小超出限制的特大塊,因此,必須臨時增加chunk size的配置值,再將特大塊均衡地分發到分區叢集中。
use configdb.settings.save({"_id":"chunksize","value":"1024"})
step3,移動特大塊
sh.moveChunk("db_name.collection_name",{sharded_filed:"value_in_chunk"},"new_shard_name")
step4,啟用均衡器
sh.setBalancerState(true)
step5,重新整理mongos的配置緩衝
強制mongos從config server同步配置資訊,並重新整理緩衝。
use admin
db.adminCommand({ flushRouterConfig: 1 } )
六,均衡器
均衡器是由mongos轉變的,就是說,mongos不僅負責將查詢路由到相應的shard上,還要負責資料區塊的均衡。一般情況下,MongoDB會自動處理資料均衡,通過config.settings能夠查看balancer的狀態,或通過sh輔助函數查看
sh.getBalancerState()
返回true,表示均衡器在正運行,系統自動處理資料均衡,使用sh輔助函數能夠關閉balancer
sh.setBalancerState(false)
balancer不能立即終止正在啟動並執行塊遷移操作,在mongos轉變為balancer時,會申請一個balancer lock,查看config.locks 集合,
use configdb.locks.find({"_id":"balancer"})
--or
sh.isBalancerRunning()
如果state=2,表示balancer正處於活躍狀態,如果state=0,表示balancer已被關閉。
均衡過程實際上是將資料區塊從一個shard遷移到其他shard,或者先將一個大的chunk拆分小的chunk,再將小塊遷移到其他shard上,塊的遷移和拆分都會增加系統的IO負載,最好將均衡器的活躍時間限制在系統空閑時進行,可以設定balancer的活躍時間視窗,限制balancer在指定的時間區間內進行資料區塊的拆分和遷移操作。
use configdb.settings.update(
{"_id":"balancer"}, "$set":{"activeWindow":{"start":"23:00","stop":"04:00"}}), true)
均衡器拆分和移動的對象是chunk,均衡器只保證chunk數量在各個shard上是均衡的,至於每個chunk包含的doc數量,並不一定是均衡的。可能存在一些chunk包含的doc數量很多,而有些chunk包含的doc數量很少,甚至不包含任何doc。因此,應該謹慎選擇分區的索引鍵,即片鍵,如果一個欄位既能滿足絕大多數查詢的需求,又能使doc數量均勻分布,那麼該欄位是片鍵的最佳選擇。
參考文檔:
Config Database
Sharding
Shards
movePrimary
Sharded Cluster Administration
Data Partitioning with Chunks
MongoDB 分區管理