分片集群
分片組件
為了能夠分擔數(shù)據(jù)庫的讀寫壓力,并且解決服務器的磁盤空間的利用情況,可以將數(shù)據(jù)分布到多臺服務器,并且每部分數(shù)據(jù)都存在副本來保證數(shù)據(jù)的高可用。實現(xiàn)這樣的一個架構(gòu)稱作分片集群。一個分片集群有多個分片組成,并且每個分片都有一個或多個副本。每個分片都是一個獨立的副本集。分片集群的結(jié)構(gòu)圖如下:
每個分片集群有三部分組成mongos、mongoconfig和mongodb。
mongos
mongos充當了路由器的角色,負責將客戶端的請求路由到分片,但是對于客戶端來說這是透明的。mongos為了提高操作數(shù)據(jù)的效率,會緩存一部分元數(shù)據(jù)。但是在mongos重啟或者新加入一個mongos或者有數(shù)據(jù)塊遷移,才會從配置服務器獲取元數(shù)據(jù)。
mongoconfig
配置服務器是一種特殊的mongod,它持有分片集群的元數(shù)據(jù),這些元數(shù)據(jù)描述了分片系統(tǒng)的狀態(tài)和組織,以及數(shù)據(jù)庫數(shù)據(jù)的分布情況。
mongodb
mongodb是用來存儲數(shù)據(jù)的真正位置,數(shù)據(jù)庫的集合會按照一定的規(guī)則分配到多個分片上,每個分片上存儲的數(shù)據(jù)只是一個集合的子集,所有分片上的數(shù)據(jù)總和為整個數(shù)據(jù)庫的數(shù)據(jù)。
分片概述
分片鍵
每個集合在分片時,需要通過一個鍵來劃分,這個鍵是文檔中任何索引的單一或者符合字段。集合分片有兩種形式:基于范圍劃分和基于哈希值劃分。
基于范圍劃分
字段值在一定范圍的文檔會落在相同的分片上,例如用戶的首字母的范圍在[a,z],在按照范圍劃分后,[a,m]在分片1,(m,z]在分片2。但是也存在一定的缺點,在某一范圍的請求過多時,會導致分片的負載過大,使系統(tǒng)沒有得到正確的擴展。
基于哈希值的劃分
哈希劃分是根據(jù)每個字段值的哈希值分配到指定的片上。這樣就會保證即使字段值相近的文檔也不會存儲在同一個分片上。這樣就會保證數(shù)據(jù)均勻的分布在分片上,但是也會存在一定的缺點,在查詢一定范圍中的文檔時,效率會降低,因為一定范圍的文檔可能會存在于多個分片。
數(shù)據(jù)塊
通過分片鍵分割的集合組成一定大小的數(shù)據(jù)塊,這些數(shù)據(jù)塊的大小默認為64MB,例如現(xiàn)在有一個博文的帖子,該集合按照日期date進行分片,集合分布在3個分片上,分片1:從最早的時間到2009年7月,分片2:從2009年8月到2011年12月,分片3:從2012年1月到現(xiàn)在。
1、客戶端在查詢數(shù)據(jù)時,首先會訪問mongos。
2、mongos會查詢本地路由表,查找該數(shù)據(jù)存在于哪個分片,并將請求發(fā)送到分片3。
3、分片3會將結(jié)果返回給mongos,符合條件的數(shù)據(jù)可能在多個分片,mongos會將所有復合條件的數(shù)據(jù)合并后返回到客戶端。
在創(chuàng)建集合時,只有一個數(shù)據(jù)塊存在于一個分片中,數(shù)據(jù)塊的范圍為(-∞,+∞),隨著數(shù)據(jù)的不斷增多,會將數(shù)據(jù)按照一定的值進行分塊。
數(shù)據(jù)平衡過程
數(shù)據(jù)塊劃分
在數(shù)據(jù)的不斷增大,mongo會根據(jù)數(shù)據(jù)塊的大小隨時進行分塊,如果數(shù)據(jù)塊的大小大于64MB就行進行分塊,使數(shù)據(jù)塊保持在小于或者等于指定大小。插入和更新操作都會觸發(fā)劃分操作。劃分操作會導致配置服務器的元數(shù)據(jù)修改。
平衡器
數(shù)據(jù)塊的劃分可能會導致集群失衡,出現(xiàn)失衡的情況會觸發(fā)平衡器。當有一個分片上的數(shù)據(jù)庫多余其他分片,平衡器會將多余的數(shù)據(jù)塊,分配到其他平衡器上,保證每個分片上的數(shù)據(jù)塊相等。集群中的任何一個mongos都可以初始化平衡器程序,在完成數(shù)據(jù)塊從一個數(shù)據(jù)塊到另一個數(shù)據(jù)塊時,會導致配置服務器上元數(shù)據(jù)的變更,同時在平衡器移動數(shù)據(jù)庫時會對數(shù)據(jù)庫性能造成影響。所以在數(shù)據(jù)塊移動時分為兩種策略:
1、可以在達到一定閾值后,觸發(fā)平衡器。這個閾值標識分片上最大數(shù)量和最小數(shù)量之間的差值。
2、在指定的時間段內(nèi)進行數(shù)據(jù)塊遷移。
數(shù)據(jù)遷移過程:
1、mongos會發(fā)送moveChunk命令到源分片。
2、源分片在接收到moveChunk命令后,會創(chuàng)建數(shù)據(jù)塊的文檔副本,并對副本進行排序。在此期間mongos接收到的客戶端請求都會繼續(xù)路由到源分片,。
3、目標分片開始從源分片接收數(shù)據(jù)副本。
4、一旦所有的數(shù)據(jù)接收完畢,就會開始初始化目標分片剛才遷移的數(shù)據(jù),以保證在此期間產(chǎn)生的數(shù)據(jù)變更。
5、一旦同步完成,源分片上的數(shù)據(jù)就會刪除。
集群片鍵
片鍵的好與壞直接關(guān)系到整個集群的性能,片鍵的選擇可能會出現(xiàn)一下集中情況:
小基數(shù)片鍵
也就是說片鍵的值只有幾個可以存在,集群只能通過這幾個值進行分片。但是隨著文檔的不斷增加,分片上的數(shù)據(jù)不斷積累,由于字段值的數(shù)量有限,最終導致分片上的數(shù)據(jù)庫的大小不斷增大。也就是說一個鍵有N個值,那就只能有N個數(shù)據(jù)塊,因此也只能有N個分片。例如有一個集合中的每個文檔都有一個字段contient這個字段,這個字段只有7個值,Asia、Europe、North America、South America、Africa、Oceania和Antarctica。但是隨著數(shù)據(jù)的不斷增多包含7個值中的任何一個值的文檔都會不斷增大,從而導致每個數(shù)據(jù)塊也在不斷增大,這樣機會失去分片的意義。
升序片鍵
如時間戳作為片鍵,隨著數(shù)據(jù)的不斷增減,數(shù)據(jù)塊從一個分為兩個,但是對數(shù)據(jù)的插入都會坐落到第二個數(shù)據(jù)塊,當?shù)诙€數(shù)據(jù)塊達到一定的大小,就會再次分為兩個,同時新數(shù)據(jù)繼續(xù)做落到最后一個數(shù)據(jù)塊,該時間戳作為的片鍵為升序片鍵。
隨機片鍵
當某一個字段的取值是隨機的,那么當數(shù)據(jù)插入時,會隨機的分配到多個分片。雖然隨著數(shù)據(jù)的不斷增多,數(shù)據(jù)均勻的分布到分片集群,但是也會對數(shù)據(jù)庫的操作效率變低。
好片鍵
一個好的分片鍵既要保證良好的數(shù)據(jù)局部性也不會因為太局部而產(chǎn)生熱點。例如{coarselyAsecond:1,search:1}這樣的組合鍵來實現(xiàn)目標,coarselyAsecond字段對應多個數(shù)據(jù)塊,但是search會在對應的多個數(shù)據(jù)塊上搜索到指定的數(shù)據(jù),也就是說coarselyAsecond字段對應局部數(shù)據(jù),search字段是一個常用的檢索字段。
使用集群
查詢中的計數(shù)功能
在查詢復合某一條件的文檔數(shù)時,mongos會將請求發(fā)送到每個分片,并將查詢的結(jié)果合并后返回給客戶端。但是當有一個數(shù)據(jù)塊在移動時,數(shù)據(jù)可能存在于兩個分片,這樣在查詢的文檔坐落在兩個分片上時,查詢的結(jié)果可能大于實際的數(shù)量。這是mongo目前存在的一個問題,可能在以后的幾個版本得到解決。
唯一索引
在分片集群中存在各種索引,但是唯一索引除了分片鍵之外難以實現(xiàn)。例如文檔{"_id":ObjectId("89sw89d89cud8we8dw"),“userName”:"abc","email":"1121111111@qq.com"},在這個文檔中如果唯一索引是username,在插入文檔時搜索所有的分片沒有userName=abc的文檔,開始插入文檔,但是此時其他的客戶端也在插入userName=abc的文檔,并查詢每個分片時也沒有出現(xiàn)userName=abc的文檔并進行插入工作,兩個客戶端同時插入userName=abc的文檔就很難保證索引的唯一性。分片鍵能夠保證唯一性是因為每個文檔會按分片鍵值存儲在一個數(shù)據(jù)塊上。由此可以了解到,在插入文檔時,自動生成的id很難保證唯一性,除非id作為分片鍵。
更新
在按照條件進行更新操作時,如果條件不是分片鍵那么在進行更新時會多所有的分片進行查詢并執(zhí)行更新操作,這樣會降低數(shù)據(jù)庫的效率。只有在使用分片鍵后,查詢會落在一個數(shù)據(jù)塊上。所以在更新數(shù)據(jù)時盡量帶有分片鍵作為條件,否則就會報錯。如果不帶有分片鍵,可以執(zhí)行批量更新來避免報錯。
基于標簽分片
1、啟動分片集群,啟動的步驟為:啟動配置服務器,啟動路由服務器,啟動分片服務器。
2、將分片標記為標簽:sh.addShardTag("分片名稱1","標簽名稱1"),...,sh.addShardTag("分片名稱N","標簽名稱N")。
3、在某一集合上的所有數(shù)據(jù)塊都放在一個分片上:sh.addTagRange("集合1",{“字段1”:"MinKey"},{"字段1":MaxKey},"標簽名稱")
4、如果在此之前使用了分片鍵,將集合均勻的分布到多個分片,此時就會發(fā)生數(shù)據(jù)塊的移動。
使用標簽進行擴展
當集群中有三個標簽(dramas、actions、comedies),三個分片(shard0000、shard0001、shard0002),三個集合(movies.drama、movies.action、movies.comedy),對應結(jié)構(gòu)如下:
現(xiàn)在需要將action分布在兩個分片上,而其他集合分布在兩外一個分片上。操作如下:
1、將標簽為dramas和comedies的標簽分布到shard0000分片上而actions分布到另外兩個分片上。sh.addShardTag("shard0000","comedies"),sh.removeShardTag("shard0002","comedies"),sh.addShardTag("shard0002","actions")。
2、在經(jīng)過一段時間后,重新查看數(shù)據(jù)塊的分片會發(fā)現(xiàn)集合movies.drama和movies.comedy的數(shù)據(jù)塊全部存在于shard0000上,集合movies.action的數(shù)據(jù)塊存在于shard0001和shard0002上。
分片操作
?1、設置多個分片服務器,并且每個分片服務器都有多個從服務器。
分片1的primary節(jié)點的配置文件:
所在的服務器的ip為192.168.121.12
dbpath=/usr/local/mongodb01/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongodb01/logs //日志存儲位置 port=27020 //實例運行端口 fork=true //是否后臺運行 replSet=db0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ip分片1的secondary節(jié)點的配置文件:
所在的服務器的ip為192.168.121.14
dbpath=/usr/local/mongodb01/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongodb01/logs //日志存儲位置 port=27019 //實例運行端口 fork=true //是否后臺運行 replSet=db0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ip分片2的arbiter節(jié)點的配置文件:
所在的服務器的ip為192.168.121.12
dbpath=/usr/local/mongodb01/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongodb01/logs //日志存儲位置 port=27021 //實例運行端口 fork=true //是否后臺運行 replSet=db0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ip設置分片1集群的配置:
rs.initiate({“_id:db0”,“members”:[{"_id":0,"host":"192.168.121.12:27020","priorty":"2"},{"_id":1,"host":"192.168.121.14:27019","priorty":"1"},{"_id":2,"host":"192.168.121.12:27021","priorty":"1","arbiterOnly" : true"}]})
2、將分片2和分片3按照分片1的操作設置為復制集。
每一個分片都是一個獨立的復制集,將所有的分片整合后就是整個mongo數(shù)據(jù)庫。
3、配置configure服務器
configure服務器與mongodb服務器沒有區(qū)別,數(shù)據(jù)庫中保存的數(shù)據(jù)為整個分片集群的配置信息。config服務器在3.2版本之后在配置時需要設置為復制集的模式。
configure服務器的primary節(jié)點配置:
所在的服務器ip為192.168.121.12
dbpath=/usr/local/mongocfg01/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongocfg01/logs //日志存儲位置 port=27018 //實例運行端口 fork=true //是否后臺運行 replSet=cfg0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ipconfigure服務器的secondary節(jié)點配置:
所在服務器的ip為192.168.121.14
dbpath=/usr/local/mongocfg02/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongocfg02/logs //日志存儲位置 port=27018 //實例運行端口 fork=true //是否后臺運行 replSet=cfg0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ipconfigure服務器的arbiter節(jié)點配置:
所在服務器的ip為192.168.121.12
dbpath=/usr/local/mongocfg03/data //數(shù)據(jù)存儲位置 logpath=/usr/local/mongocfg03/logs //日志存儲位置 port=27019 //實例運行端口 fork=true //是否后臺運行 replSet=cfg0 //復制集的名稱 journal=true //否否開啟journal日志 bind_ip=0.0.0.0 //綁定的ipconfigure服務器復制集的配置:
rs.initiate({“_id:db0”,“members”:[{"_id":0,"host":"192.168.121.12:27018","priorty":"2"},{"_id":1,"host":"192.168.121.14:27018","priorty":"1"}, {"_id":2,"host":"192.168.121.12:27019","priorty":"1","arbiterOnly" : true"}]})
4、配置mongos路由服務器
在配置路由服務器時,可以配置多個以此保證當其中一個服務器掛掉后,整個業(yè)務系統(tǒng)能夠正常運行。
配置內(nèi)容如下:
路由服務器的ip為192.168.121.12和192.168.121.14
logpath=/usr/local/mongos01/logs port=27017 fork=true configdb=cfg0/192.168.121.12:27018,192.168.121.12:27019,192.168.121.14:27018 bind_ip=0.0.0.05、添加分片到集群
sh.addShard("db0/192.168.121.12:27020")
sh.addShard("db1/192.168.121.12:27022")
sh.addShard("db2/192.168.121.14:27021")
執(zhí)行sh.status()發(fā)現(xiàn)所有的從節(jié)點也會主動加入到分片集群中。
6、啟動分片集群
啟動所有的配置服務器
啟動所有的路由服務器
啟動所有的分片服務器
7、添加一個新分片
設置新分片的復制集:primary節(jié)點為192.168.121.14:27023,secondary節(jié)點為192.168.121.12:27024,arbiter節(jié)點為192.168.121.14:27024
添加新分片到分片集群:db.addShard("db3/192.168.121.14:27024')
驗證是否添加成功,運行db.runCommand({listshards:1})
查看集合中文檔的數(shù)量,會發(fā)現(xiàn)結(jié)合數(shù)量在不斷增加,此時mongos在進行數(shù)據(jù)塊的平衡。
8、移除一個分片
在移除分片后,mongos會自動將該分片上的數(shù)據(jù)塊移除,然后才執(zhí)行移除分片的命令。
執(zhí)行移除分片的命令為:db.runCommand({removeShard:"192.168.121.14:27024"})
執(zhí)行該命令后,經(jīng)過一段時間,繼續(xù)執(zhí)行移除分片的指令,返回的結(jié)果中有一個state字段,值為“ongoing”標識數(shù)據(jù)塊在遷移過程中,值為"complete"標識數(shù)據(jù)塊遷移完成。
可以執(zhí)行l(wèi)istshards指令來查看分片集群中各個分片的狀態(tài):db.runCommand({listshards:1}),就會發(fā)現(xiàn)移除的分片已經(jīng)不再分片集群中。
9、創(chuàng)建數(shù)據(jù)庫并添加分片集合
首先在進群中創(chuàng)建數(shù)據(jù)庫eshop和集合users,并插入文檔,此時集合只在一個分片上存儲,因為沒有進行集合分片。
使數(shù)據(jù)庫支持分片:sh.enableSharding("eshop")
由于需要為分片集合提供分片鍵,并且分片鍵必須是索引,所以需要在分片鍵上創(chuàng)建索引:db.users.ensureIndex({city:1})。如果分片鍵是復合鍵,就需要創(chuàng)建復合索引。
為分片結(jié)合設置分片鍵:sh.shardCollection("eshop.users",{city:1})。
轉(zhuǎn)載于:https://www.cnblogs.com/youzhongmin/p/8306969.html
總結(jié)
- 上一篇: 仿抖音短视频系统源码,获取系统图片
- 下一篇: iOS GitHub上常用第三方框架与一