對于數據量和請求量很大的業務場景,可以使用文檔數據庫服務集群版本來提升容量和性能。
集群版本包含的 3 種組件:
- mongos 節點。用戶通過 mongos 節點操作數據庫,mongos 會將用戶的操作請求路由到底層的存儲分片中,并收集結果進行合并等操作后趕回給客戶端。
- config server 節點。底層是 3 副本主從架構,存儲路由表、用戶、集群配置等信息。對于用戶來說是不感知的。
- shard server 節點。分片集群有 2 個或者多個 shard,每個 shard 都是獨立的 3 副本主從架構。每個 shard server 存儲具體的用戶數據,執行具體的數據操作。
創建分片表
如果使用集群版本,用戶可以在建表時指定片建創建分片表。后續對分片表的讀寫操作會根據指定的分片建路由到合適的 shard server 中。
需要注意的是,集群版本也能創建非分片表。如果用戶在連接 mongos 之后沒有按照分片表的方式建表,默認會創建一個非分片表,并存儲在某一個 shard server 上,此時容量和性能都會受限。
選擇分片策略
文檔數據庫服務集群常用的分片方式有范圍和哈希方式。
- 范圍分片
范圍分片方式會按照分片鍵的范圍將數據分成多個 chunk,每個 chunk (默認配置是 64MB) 存儲一部分范圍連續的數據。范圍分片能夠很好地滿足范圍查詢需求,但是缺點也很明顯。如果業務的寫入模型有明顯按照分片建遞增或者遞減的趨勢,則寫入操作很大程度上會分布再同一個 shard 上,導致無法擴展寫能力。
- 哈希分片
哈希分片方式會先計算分片鍵的哈希值,然后再根據哈希值的取值范圍進行分片,將文檔分布到不同的 chunk 中。基于哈希分片能夠保證數據在各個分片上的分布基本均衡。但是缺點在于如果出現范圍查詢,mongos 節點需要將請求廣播到 shard server 上,導致查詢效率會有所下降。
選擇合理的分片鍵
分片鍵的選取需要保障數據分布足夠離散,每個分片的存儲容量均衡,并能夠將數據操作均勻分發到集群中的所有分片中。
如果分片鍵選取不佳,可能會導致各個分片負載不均,出現 jumbo chunk 導致無法分裂等問題。而且分片鍵一旦確定之后,不能在運行過程中進行變更,需要按新分片鍵創建新表后重新導入數據。
一般選取分片鍵時,會考慮以下因素:
- 分片鍵的區分度
分片鍵的取值基數決定了最多能包含多少個 chunk,如果取值基數太小,則會導致 chunk 數量很低,可能會有負載不均的問題。比如按照“性別”來設置分片鍵就不是合理的選擇,因為 “性別” 只有 “男”、“女” 2 種取值,這樣最多 2 個 chunk。
- 分片鍵的取值分布是否均勻
如果分片鍵的取值存在熱點,也可能導致分片負載不均。比如以 “國家” 作為片建,會由于各個國家之間人口的差異出現負載不均,人口多的國家存儲量大請求多,而人口少的國家存儲量小請求少。對于這種場景,可以考慮使用復合鍵來作為分片鍵,降低出現熱點的概率。
- 是否會按照分片鍵單調寫入
如果業務按照分片鍵遞增或者遞減的趨勢進行讀寫,則可能在某一時刻請求集中在某個 chunk 上,無法發揮集群多個分片的優勢。比如對于存儲日志的場景,如果按照日志創建時間進行范圍分片,則在某一時間內一直對同一個 chunk 進行寫入。對于這種場景,可以考慮復合分片鍵或者哈希分片來避免這種情況。
- 查詢模型是否包含分片鍵
在確定分片鍵后,需要考慮業務的查詢請求中是否包含有分片鍵。mongos 節點會根據查詢請求中的分片鍵將請求轉發到對應的 shard server 中。如果查詢請求中不包含分片鍵,則 mongos 節點會將請求廣播到后端的所有 shard server,進行 scatter/gather 模式的查詢,此時查詢性能會有所下降。
創建分片表和相關注意事項
使用 mongo shell 客戶端連接到 mongos 節點,創建分片表的步驟和命令如下。
- 設置對應的 database 為分片模式。
sh.enableSharding(<database>) - 指定分片鍵創建分片表。
sh.shardCollection(<namespace>, <key>, <unique>, <options>)
命令中其中參數說明:
- namespace:是 . 的形式,比如 "mydb.myshardcollection"。
- key:表示分片鍵和分片策略,1 表示范圍分片,"hashed" 表示哈希分片。比如 {"myshardKey": "hashed"}。
- unique:表示分片鍵是否有全局唯一性約束,true 表示唯一,對于哈希分片來說只能是 false。
- options:表示分片的參數選項,對于哈希分片來說可以指定預分配的 chunk 個數,比如 {numInitialChunks: 5}。
每個 chunk 的默認大小是 64MB, 對于哈希分片可以通過 numInitialChunks 預分配 chunk,能夠有效降低寫入過程中 chunk 分裂遷移帶來的性能抖動。
配置 balancer 窗口
分片集群內部有 balancer 模塊進行數據均衡,保證每個 shard 的 chunk 個數大體相同。Balancer 操作涉及到 chunk 遷移,因此也會占用 shard server 上的硬件資源。
用戶可以通過配置 balancer 窗口來指定后臺哪些時間段進行數據均衡操作,通過將均衡操作指定在業務低峰期,可以有效避免對線上業務的影響。
設置和步驟和方法如下:
- 使用 mongo shell 連接到 mongos 節點。
- 切換到 config 數據庫(use config)。
- 確認 balancer 開關是開啟的(sh.getBalancerState())。
- 設置 balancer 時間窗口。
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "<起始時間>", stop : "<結束時間>" } } },
{ upsert: true }
)
其中 “起始時間” 和 “結束時間” 都是 HH:MM 的形式,比如 02:30。