Mongo
復制和分片
創建副本集
建立副本集
使用mongo --nodb選項啟動mongo shell,啟動shell但是不連接到任何mongod
$ mongo --nodb
創建副本集
replicaSet=new ReplSetTest({
"nodes":3
})
會創建一個包含三個服務器的副本集:一個住服務器和兩個備份服務器
指定命令使mongod服務器啟動
replicaSet.startSet()
replicaSet.initiate()
然后會啟動三個mongod進行,分別運行在31000\31001和31002端口
連接到運行在31000端口的mongod
conn1=new Mongo("127.0.0.1:31000")
> primary=conn1.getDB("test")
在連接到主節點的連接上執行isMaster命令,可以看到副本集的狀態
> primary.isMaster()
在主節點插入一些文檔,
備份節點可能會落后于主節點,可能沒有最新寫入的數據,所以備份節點在默認情況下會拒絕讀取請求,以防應用意外拿到過期的數據。當在備份節點做查詢時,可能會得到一個錯誤提示,說當前節點不是主節點
如果希望從備份節點讀取數據,需要設置“從備份節點讀取數據沒有問題”的標示
conn2.setSlaveOk()
然后就可以在該連接的備份節點中讀取數據
不能對備份節點執行寫操作,備份節點只能通過復制功能寫入數據,不接受客戶端的寫入請求
具有自動故障轉移的功能,如果主節點掛了,其中一個備份節點會自動選舉為主節點
配置副本集
首先需要為副本集選定一個名字,使用--replset name選項重啟server
$ mongod --replSet spock -f mongod.conf --fork
然后使用同樣的replset和標識符stock再啟動兩個mongod服務器作為副本集中的其他成員
# server2
$ mongod --replSet spck -f mongo.conf --fork
#server3
$ mongod --replSet spck -f mongo.conf --fork
只要將后兩個成員添加到副本集中,它們就會自動克隆第一個成員的數據
將replSet選項添加到每個成員的mongod.conf文件中,啟動時就會自動使用這個選項
為了讓每個mongod能夠知道批次的存在,需要創建一個配置文件,在配置文件中列出每一個成員,并且將配置文件發送給server-1,然后server-1會負責將配置文件傳播給其他成員
在shell中創建一個如下所示的文檔
config={
"_id":"spock",
"members"[
{
"_id":0,
"host":"server-1:27017"
},
{
"_id":1,
"host":"server-2:27017"
},
{
"_id":2,
"host":"server-3:27017"
}
]
}
- 第一個_id字段就是副本集名稱
- 將host字段的值修改為實際ip
這個config對象就是副本集的配置,現在需要將其發送給其中一個副本集成員,連接到一個有效的服務器,使用config對象對副集進行初始化
// 連接到server1
db=(new Mongo("server-1:27017")).getDB("test")
// 初始化副本集
rs.initiate(config)
server-1會解析這個配置對象,然后向其他成員發送消息,提醒他們使用新的配置,所有車公園配置完成之后,他們會自動選出一個主節點,然后就可以正常處理請求了
rs輔助函數
rs是一個全局變量,其中包含與復制相關的函數
網絡注意事項
副本集內的每個成員都必須能夠連接到其他所有成員
副本集的配置中不應該使用localhost作為主機
修改副本集配置
可以隨時修改副本集的配置,可以添加或者刪除成員,也可以修改已有成員
//向副本集中添加成員
rs.add("server-4:27017")
// 刪除成員
rs.remove("server-1:27017")
// 查看配置
rs.config()
// 重新配置配置文件
rs.reconfig(config)
副本集的組成
同步
復制用于在多臺服務器之間備份數據,mongo的復制功能時使用操作日志oplog實現的,操作日志包含了主節點的每一次寫操作。
如果某個備份節點由于某些原因掛掉,當他重新啟動后,就會自動從oplog中最后一個操作開始進行同步
初始化同步
副本集中的成員啟動之后,就會檢查自身狀態,確定是否可以從某個成員那里進行同步,如果不行的話,它會嘗試從副本的另一個成員那里進行完整的數據復制,這個過程就是初始化同步
處理陳舊數據
如果備份節點遠遠落后同步源當前的操作,那么這個備份節點就是陳舊的。如果備份節點曾靜停機過,寫入量炒股喲自身處理能力,或者時有太多的讀請求,這些情況都有可能導致備份節點陳舊
當一個節點陳舊時候,它會查詢副本中的其他成員,如果成員的oplog足夠詳盡,可以用于處理那些落下的操作,就從這個成員進行同步,如果都沒有參考價值,那么這個成員的復制操作就會中止,這個成員需要重新進行完全同步。
心跳
為了維護集合的最新視圖,每個成員每隔2s就會向其他成員發送一個心跳請求。用于檢查每個成員的狀態
成員狀態
- STARTUP:成員剛啟動時出于這個狀態,會嘗試加在成員的副本集配置,加在成功后,就進入STARTUP2狀態
- STARTUP2:整個初始化同步過程都處理這個狀態,但是如果在普通成員上,這個狀態只會持續幾秒種。在這個狀態下,mongodb會創建幾個線程,用于處理復制和選舉,然后切換到RECOVERING狀態
- 表示成員運轉正常,但是暫時還不能處理讀取請求
- ARBITER:在正常的操作中,仲裁者應該處理ARBITER狀態
系統出現問題時會處理下面這些狀態:
- DOWN:如果一個正常運行的成員變得不可達,它就出于DOWN狀態
- UNKNOWN:如果一個成員無法到達其他任何成員,其他成員就無法知道它處理什么狀態,會將其報告為UNKOWN狀態
- REMOVE:當成員被移出副本集時,就出于這個狀態
- ROLLBACK:當成員正在進行數據回滾,就出于ROLLBACK狀態,回滾過程結束時,服務器會轉換為RECOVERING狀態,然后成為備份節點
- FATAL:如果一個成員發生了不可挽回的錯誤,也不再嘗試恢復正常的話,它就出于FATAL狀態
選舉
當一個成員無法到達主節點時,它就會申請被選舉為主節點。希望唄選舉為主節點的成員,會向它能到達的所有成員發送通知如果這個成員不符合候選人要求,其他成員可能知道相關原因
假如沒有反對的理由,其他成員就會對這個成員進行選舉投票。如果這個成員得到副本大多數贊成票,它就會選舉成功,會轉到主節點狀態。如果 達不到大多數要求,那就出于備份節點狀態,之后還可以再次申請唄選舉為主節點,主節點會一致出于主節點狀態。除非它不在滿足大多數的要求或者掛了而退位。
從應用程序連接副本集
客戶端到副本集的連接
從應用程序的角度,使用副本集與使用單臺服務器一致。默認情況下,驅動程序連接到主節點,并且將所有路由都路由到主節點。應用程序可以像是有那個單臺服務器一樣進行讀寫,副本集會在后臺處理熱備份
連接副本集與連接單臺服務器非常想,一個常用的連接字符串如下:
mongodb://server-1:27017,server-2:27017
當主節點掛掉之后,驅動程序會盡快自動找到新的主節點,在選舉過程中,主節點可能會暫時不可用,如果沒有可達的成員能夠成為主節點,主節點可能長時間不可用
等待寫入復制
使用getLastError命令檢查寫入是否成功,也可以使用這個命令確保寫入操作唄復制到備份節點,參數w會強制要求getLastError等待,一直到給定數量的成員都執行完了最后的寫入操作。
db.runCommand({
"getLastError":1,
"w":"majority"
})
可以指定wtimeout的值設置超時時間,如果這個超過這個時間還沒有返回,就會返回失敗
設置超時時間為1s
db.runCommand({
"getLastError":1,
"w":"majority",
"wtimeout":1000
})
分片
分片是指將數據拆分,將其分散存放在不同的機器上的過程。
幾乎所有數據庫都能進行手動分片,但mongo支持自動分片,可以使數據庫架構對應用程序不可見,也可以簡化系統管理。對應用來說,和使用單機mongo服務器一樣。
在分片之前需要先執行mongos進行一次路由過程
快速建立一個簡單集群
使用-nodb選項啟動mongo shell
$ mongo --nodb
使用ShardingTest創建集群
cluster=new ShardingTest({
"shards":3,
"chunksize":1
})
會創建包含三個切片的集群,分別運行在30000,30001,30002端口,默認情況下ShardingTest會在30999端口啟動mongos
連接到mongos使用集群
db=(new Mongo("127.0.0.1:30999")).getDB("test")
接下來和使用單機服務器完全一樣
使用sh.status()可以查看集群的狀態,分片摘要信息、數據庫摘要信息、集合摘要信息
主分片是為每個數據庫隨機選擇的,所有數據都會位于主分片上。目前還不能自動將數據分發到不同的分片上,因為它不知道你希望如何分發數據。對每一個集合,必須明確指定,應該如何分發數據。
要求一個集合分片,首先要對這個集合的數據庫啟用分片,執行下列命令
sh.enableSharding("test")
對集合分片時,要 選擇一個片鍵。片鍵時集合的一個鍵,mongodb根據這個鍵拆分數據。(例如,如果選擇基于“username”進行分片,mongo會根據不同的用戶名進行分片) 選擇片鍵可以認為時選擇集合中的數據的順序。它與索引時個相似的概念;隨著集合的不斷增長,片鍵就會成為集合上最重要的索引。只有唄索引過的鍵才能作為片鍵
在啟用分片時,先在希望作為片鍵的鍵上創建索引
db.users.ensureIndex({
"username":1
})
然后對集合進行分片
sh.shardCollection("test.users",{
"username":1
})
集合會被分為讀個數據塊,每一個數據塊都是集合的一個數據子集
包含片鍵的查詢能夠直接被發送到目標分片或者是集群分片的一個子集,這樣的查詢叫定向查詢
有些查詢必須被發送到所有分片,這樣的查詢叫分散-聚集查詢,mongo將查詢分散到所有分片上,然后將各個分片的查詢結果聚集起來。
關閉集群
cluster.stop()
配置分片
何時分片
通常不必太早分片,因為分片不僅會增加部署的操作復雜度,還要求作出設計決策,而該決策以后很難再改。另外最好也不要在系統運行太久之后在分片。
分片用來:
- 增加可用RAM
- 增加可用磁盤空間
- 減輕單臺服務器的負載
- 處理單個mongod無法承受的吞吐量
啟動服務器
配置服務器
配置服務器相當于集群的大腦,保存著集群和分片的元數據,即各分片包含哪些數據的信息,因此,應該首先建林配置服務器
啟動配置服務器
$ mongod --configscr --dbpath /var/lib/mongodb -f /var/lib/config/mongod.conf
- 啟動配置服務器時,不要使用--replSet選項:配置服務器不是副本集成員
- --configscr 指定mongod為新的配置服務器,該配置將mongod的默認監聽端口改為27019,并吧默認的數據目錄改為/data/confgdb(可使用--port和--dbpath選項修改這兩項配置)
mongos進程
當服務器出于運行狀態后,啟動一個mongos進程提供應用程序連接。mongos進程需要知道配置服務器的地址,所以必須使用--configdb選項啟動mongos
$ mongos -configdb config-1:279019,config-2:279019.config-3:279019 -f /var/lib/mongos.conf
將副本集轉換為分片
假設我們已經擁有了一個副本集
如果已經有一個使用中的副本集,該副本集會成為第一個分片。為了將副本集轉換為分片,需告知mongos副本集名稱和副本集成員列表
例如在server-1到server-5上有一個名為spock的副本集,可連接到mongos并運行:
sh.addShard("spock/server-1:27017,server-2:27017,server-4:27017")
可在參數中指定副本集的所有成員。如果運行sh.status(),可發現mongodb已經找到了其他的副本集成員
也可以創建但mongod服務器的分片(而不是副本集分片),直接在addShard()中指定單個mongod的主機名和端口
sh.addShard("some-server:27017")
數據分片
除非明確指定規則,否則mongodb不會自動對數據進行拆分,如有必要,必須明確告知數據庫和集合。
假設我們希望對music數據庫中的artists集合按照name鍵進行分片。首先,對music數據庫啟用分片
db.enableSharding("music")
對數據庫分片是對集合分片的先決條件
對數據庫啟用分片之后,就可以使用shardCollection()命令對集合分片了
sh.shardCollection("music.artists",{
"name":1
})
- 如果對已存在的集合進行分片,那么name鍵上必須有索引
- 如果進行分片的集合還不存在,mongos會自動在片鍵上創建索引
均衡器
均衡器負責數據的遷移,它會周期性的檢查分片-是否存在不均衡,如果存在,就會開始快的遷移
選擇片鍵
檢查使用情況
對集合進行分片,要選擇一或兩個字段用于拆分數據。這個鍵就叫做片鍵
數據分發
數據分發有三種:
- 升序片鍵
- 隨機分發片鍵
- 基于位置的片鍵
升序片鍵
升序片鍵類似于"date"字段或者是objectId,是一種會隨著時間穩定增長的字段。
隨機分發的片鍵
隨機分發的鍵可以是用戶名、郵件地址、uuid、md5或者是數據集中其他沒有規律的鍵
基于位置的片鍵
基于位置的片鍵可以是用戶的ip、經緯度或者地址。位置片鍵不必與實際的物理位置字段相關。數據會根據這個位置進行分組。
片鍵策略
散列片鍵
如果追求的是數據加在速度的極值,那么散列片鍵時最佳選擇。散列片鍵可使其他任何鍵隨機分發。所以,如果打算在大量查詢中使用升序鍵,但又同時希望吸入數據隨機分發的話,散列片鍵會是個好選擇。
弊端時無法使用散列片鍵作為指定目標的范圍查詢。
創建散列片鍵,首先要創建散列索引
db.users.ensureIndex({
"username":"hashed"
})
然后對集合分片
db.shardCollection("app.users",{
"username":"hashed"
})
局限性:
- 不能使用enique選項
- 不能使用數組字段
- 浮點型的值會先唄取整,然后才會進入散列,所有1和1.99999會得到相同的散列值
GridFS的散列片段
GridFS集合通常非常適合做分片,因為它們包含大量的文件數據
在files_id字段上創建散列索引,則每個文件都會隨機分發到集群中,但是一個文件只能唄包含在一個單一的塊中,這時非常好的
為了實現這種策略,需要在{"files_id":"hashed"}創建新的索引,然后依據這個字段對集合分片
片鍵規則和指導方陣
片鍵限制
片鍵不可以是數組,向片鍵插入數據值也是不被允許的
文檔一旦插入,其片鍵值就無法修改了。因此應該選擇不會唄改變的字段,或者時很少發生變化的字段
大多數特殊類型的索引都不能用作片鍵
片鍵的勢
不管片鍵時跳躍增長還是穩定增長,選擇一個值發生變化的鍵時非常重要的。與索引一樣,分片在勢比比較高的字段性能更佳
注:
- 上述測試在MongoDB 3.4.3-8-g05b19c6中成功
- 上述文字皆為個人看法,如有錯誤或建議請及時聯系我