【轉】MongoDB 分片的原理、搭建、應用

来源:http://www.cnblogs.com/wuhl-89/archive/2017/05/12/6846056.html
-Advertisement-
Play Games

一、概念: 分片(sharding)是指將資料庫拆分,將其分散在不同的機器上的過程。將數據分散到不同的機器上,不需要功能強大的伺服器就可以存儲更多的數據和處理更大的負載。基本思想就是將集合切成小塊,這些塊分散到若幹片里,每個片只負責總數據的一部分,最後通過一個均衡器來對各個分片進行均衡(數據遷移)。 ...


 

一、概念:

      分片(sharding)是指將資料庫拆分,將其分散在不同的機器上的過程。將數據分散到不同的機器上,不需要功能強大的伺服器就可以存儲更多的數據和處理更大的負載。基本思想就是將集合切成小塊,這些塊分散到若幹片里,每個片只負責總數據的一部分,最後通過一個均衡器來對各個分片進行均衡(數據遷移)。通過一個名為mongos的路由進程進行操作,mongos知道數據和片的對應關係(通過配置伺服器)。大部分使用場景都是解決磁碟空間的問題,對於寫入有可能會變差(+++裡面的說明+++),查詢則儘量避免跨分片查詢。使用分片的時機:

1,機器的磁碟不夠用了。使用分片解決磁碟空間的問題。
2,單個mongod已經不能滿足寫數據的性能要求。通過分片讓寫壓力分散到各個分片上面,使用分片伺服器自身的資源。
3,想把大量數據放到記憶體里提高性能。和上面一樣,通過分片使用分片伺服器自身的資源。

二、部署安裝前提是安裝了mongodb(本文用3.0測試)

在搭建分片之前,先瞭解下分片中各個角色的作用。

① 配置伺服器。是一個獨立的mongod進程,保存集群和分片的元數據,即各分片包含了哪些數據的信息。最先開始建立,啟用日誌功能。像啟動普通的mongod一樣啟動配置伺服器,指定configsvr選項。不需要太多的空間和資源,配置伺服器的1KB空間相當於真是數據的200MB。保存的只是數據的分佈表。當服務不可用,則變成只讀,無法分塊、遷移數據。
② 路由伺服器。即mongos,起到一個路由的功能,供程式連接。本身不保存數據,在啟動時從配置伺服器載入集群信息,開啟mongos進程需要知道配置伺服器的地址,指定configdb選項。
③ 分片伺服器。是一個獨立普通的mongod進程,保存數據信息。可以是一個副本集也可以是單獨的一臺伺服器。

部署環境:3台機子

A:配置(3)、路由1、分片1;

B:分片2,路由2;

C:分片3

      在部署之前先明白片鍵的意義,一個好的片鍵對分片至關重要。片鍵必須是一個索引,數據根據這個片鍵進行拆分分散。通過sh.shardCollection加會自動創建索引。一個自增的片鍵對寫入和數據均勻分佈就不是很好,因為自增的片鍵總會在一個分片上寫入,後續達到某個閥值可能會寫到別的分片。但是按照片鍵查詢會非常高效。隨機片鍵對數據的均勻分佈效果很好。註意儘量避免在多個分片上進行查詢。在所有分片上查詢,mongos會對結果進行歸併排序。

啟動上面這些服務,因為在後臺運行,所以用配置文件啟動,配置文件說明

1)配置伺服器的啟動。(A上開啟3個,Port:20000、21000、22000) 

配置伺服器是一個普通的mongod進程,所以只需要新開一個實例即可。配置伺服器必須開啟1個或則3個,開啟2個則會報錯

BadValue need either 1 or 3 configdbs

因為要放到後臺用用配置文件啟動,需要修改配置文件:

/etc/mongod_20000.conf

#數據目錄
dbpath=/usr/local/config/
#日誌文件
logpath=/var/log/mongodb/mongodb_config.log
#日誌追加
logappend=true
#埠
port = 20000
#最大連接數
maxConns = 50
pidfilepath = /var/run/mongo_20000.pid
#日誌,redo log
journal = true
#刷寫提交機制
journalCommitInterval = 200
#守護進程模式
fork = true
#刷寫數據到日誌的頻率
syncdelay = 60
#storageEngine = wiredTiger
#操作日誌,單位M
oplogSize = 1000
#命名空間的文件大小,預設16M,最大2G。
nssize = 16
noauth = true
unixSocketPrefix = /tmp
configsvr = true

/etc/mongod_21000.conf

數據目錄
dbpath=/usr/local/config1/
#日誌文件
logpath=/var/log/mongodb/mongodb_config1.log
#日誌追加
logappend=true
#埠
port = 21000
#最大連接數
maxConns = 50
pidfilepath = /var/run/mongo_21000.pid
#日誌,redo log
journal = true
#刷寫提交機制
journalCommitInterval = 200
#守護進程模式
fork = true
#刷寫數據到日誌的頻率
syncdelay = 60
#storageEngine = wiredTiger
#操作日誌,單位M
oplogSize = 1000
#命名空間的文件大小,預設16M,最大2G。
nssize = 16
noauth = true
unixSocketPrefix = /tmp
configsvr = true

開啟配置伺服器:

root@mongo1:~# mongod -f /etc/mongod_20000.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 8545
child process started successfully, parent exiting

root@mongo1:~# mongod -f /etc/mongod_21000.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 8595
child process started successfully, parent exiting

同理再起一個22000埠的配置伺服器。

#數據目錄
dbpath=/usr/local/config2/
#日誌文件
logpath=/var/log/mongodb/mongodb_config2.log
#日誌追加
logappend=true
#埠
port = 22000
#最大連接數
maxConns = 50
pidfilepath = /var/run/mongo_22000.pid
#日誌,redo log
journal = true
#刷寫提交機制
journalCommitInterval = 200
#守護進程模式
fork = true
#刷寫數據到日誌的頻率
syncdelay = 60
#storageEngine = wiredTiger
#操作日誌,單位M
oplogSize = 1000
#命名空間的文件大小,預設16M,最大2G。
nssize = 16

noauth = true
unixSocketPrefix = /tmp

configsvr = true
View Code

2)路由伺服器的啟動。(A、B上各開啟1個,Port:30000)

路由伺服器不保存數據,把日誌記錄一下即可。

# mongos

#日誌文件
logpath=/var/log/mongodb/mongodb_route.log
#日誌追加
logappend=true
#埠
port = 30000
#最大連接數
maxConns = 100
#綁定地址
#bind_ip=192.168.200.*,...,

pidfilepath = /var/run/mongo_30000.pid

configdb=192.168.200.A:20000,192.168.200.A:21000,192.168.200.A:22000  #必須是1個或則3個配置 。
#configdb=127.0.0.1:20000  #報錯
#守護進程模式 fork = true

其中最重要的參數是configdb,不能在其後面帶的配置伺服器的地址寫成localhost或則127.0.0.1,需要設置成其他分片也能訪問的地址,即192.168.200.A:20000/21000/22000。否則在addshard的時候會報錯:

{
"ok" : 0,
"errmsg" : "can't use localhost as a shard since all shards need to communicate. either use all shards and configdbs in localhost or all in actual IPs  host: 172.16.5.104:20000 isLocalHost:0"
}

開啟mongos:

root@mongo1:~# mongos -f /etc/mongod_30000.conf 
2015-07-10T14:42:58.741+0800 W SHARDING running with 1 config server should be done only for testing purposes and is not recommended for production
about to fork child process, waiting until server is ready for connections.
forked process: 8965
child process started successfully, parent exiting

3)分片伺服器的啟動:

就是一個普通的mongod進程:

root@mongo1:~# mongod -f /etc/mongod_40000.conf 
note: noprealloc may hurt performance in many applications
about to fork child process, waiting until server is ready for connections.
forked process: 9020
child process started successfully, parent exiting

A伺服器上面的服務開啟完畢

root@mongo1:~# ps -ef | grep mongo
root      9020     1  0 14:47 ?        00:00:06 mongod -f /etc/mongod_40000.conf
root      9990     1  0 15:14 ?        00:00:02 mongod -f /etc/mongod_20000.conf
root     10004     1  0 15:14 ?        00:00:01 mongod -f /etc/mongod_21000.conf
root     10076     1  0 15:20 ?        00:00:00 mongod -f /etc/mongod_22000.conf
root     10096     1  0 15:20 ?        00:00:00 mongos -f /etc/mongod_30000.conf

按照上面的方法再到B上開啟分片服務和路由服務(配置文件一樣),以及在C上開啟分片服務。到此分片的配置伺服器、路由伺服器、分片伺服器都已經部署完成。

三、配置分片:下麵的操作都是在mongodb的命令行里執行

1)添加分片sh.addShard("IP:Port") 

登陸路由伺服器mongos 操作

root@mongo1:~# mongo --port=30000
MongoDB shell version: 3.0.4
connecting to: 127.0.0.1:30000/test
mongos> 

添加分片:

mongos> sh.status()    #查看集群的信息
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("559f72470f93270ba60b26c6")
}
  shards:
  balancer:
    Currently enabled:  yes
    Currently running:  no
    Failed balancer rounds in last 5 attempts:  0
    Migration Results for the last 24 hours: 
        No recent migrations
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }

mongos> sh.addShard("192.168.200.A:40000") #添加分片
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("192.168.200.B:40000") #添加分片
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.addShard("192.168.200.C:40000") #添加分片
{ "shardAdded" : "shard0002", "ok" : 1 }

mongos> sh.status()    #查看集群信息
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("559f72470f93270ba60b26c6")
}
  shards:  #分片信息
    {  "_id" : "shard0000",  "host" : "192.168.200.A:40000" }
    {  "_id" : "shard0001",  "host" : "192.168.200.B:40000" }
    {  "_id" : "shard0002",  "host" : "192.168.200.C:40000" }
  balancer:
    Currently enabled:  yes
    Currently running:  no
    Failed balancer rounds in last 5 attempts:  0
    Migration Results for the last 24 hours: 
        No recent migrations
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }

2)開啟分片功能:sh.enableSharding("庫名")、sh.shardCollection("庫名.集合名",{"key":1})

mongos> sh.enableSharding("dba")  #首先對資料庫啟用分片
{ "ok" : 1 }
mongos> sh.status()               #查看分片信息
--- Sharding Status ---...
... databases: {
"_id" : "admin", "partitioned" : false, "primary" : "config" } { "_id" : "test", "partitioned" : false, "primary" : "shard0000" } { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" } mongos> sh.shardCollection("dba.account",{"name":1}) #再對集合進行分片,name欄位是片鍵。片鍵的選擇:利於分塊、分散寫請求、查詢數據。 { "collectionsharded" : "dba.account", "ok" : 1 } mongos> sh.status() --- Sharding Status ---... shards: { "_id" : "shard0000", "host" : "192.168.200.51:40000" } { "_id" : "shard0001", "host" : "192.168.200.52:40000" } { "_id" : "shard0002", "host" : "192.168.200.53:40000" } ... databases: { "_id" : "admin", "partitioned" : false, "primary" : "config" } { "_id" : "test", "partitioned" : false, "primary" : "shard0000" } { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" } #庫 dba.account shard key: { "name" : 1 } #集合 chunks: shard0000 1 { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)

上面加粗部分表示分片信息已經配置完成。要是出現:

too many chunks to print, use verbose if you want to force print

想要看到詳細的信息則需要執行:

mongos> sh.status({"verbose":1})
或則
mongos> db.printShardingStatus("vvvv")
或則
mongos> printShardingStatus(db.getSisterDB("config"),1)

四、測試 :對dba庫的account集合進行測試,隨機寫入,查看是否分散到3個分片中。

判斷是否為shard:db.runCommand({isdbgrid:1})

mongos> db.runCommand({isdbgrid:1})
{ "isdbgrid" : 1, "hostname" : "mongo3c", "ok" : 1 }

通過一個python腳本進行隨機寫入:分別向A、B 2個mongos各寫入10萬條記錄。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#隨即寫MongoDB Shard 測試

import pymongo
import time
from random import Random
def random_str(randomlength=8):
    str = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        str+=chars[random.randint(0, length)]
        return str

def inc_data(conn):
    db = conn.dba
#    db = conn.test
    collection = db.account
    for i in range(100000):
        str = ''
        chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
        length = len(chars) - 1
        random = Random()
        for i in range(15):
            str+=chars[random.randint(0, length)]
            string = str
        collection.insert({"name" : string, "age" : 123+i, "address" : "hangzhou"+string})

if __name__ =='__main__':
    conn = pymongo.MongoClient(host='192.168.200.A/B',port=30000)

    StartTime = time.time()
    print "===============$inc==============="
    print "StartTime : %s" %StartTime
    inc_data(conn)
    EndTime = time.time()
    print "EndTime : %s" %EndTime
    CostTime = round(EndTime-StartTime)
    print "CostTime : %s" %CostTime
View Code

查看是否分片:db.collection.stats()

mongos> db.account.stats() #查看集合的分佈情況
...
...
"shards" : { "shard0000" : { "ns" : "dba.account", "count" : 89710, "size" : 10047520, ...
...
"shard0001" : { "ns" : "dba.account", "count" : 19273, "size" : 2158576, ...
...
"shard0002" : { "ns" : "dba.account", "count" : 91017, "size" : 10193904, ...
...

上面加粗部分為集合的基本信息,可以看到分片成功,各個分片都有數據(count)。到此MongoDB分片集群搭建成功。

++++++++++++++++++++++++++++++++++++++++++++++++

感興趣的同學可以看下麵這個比較有趣的現象:

#在寫之前分片的基本信息:
mongos> sh.status()
--- Sharding Status --- 
...
...
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
    {  "_id" : "dba",  "partitioned" : true,  "primary" : "shard0000" }
        dba.account
            shard key: { "name" : 1 }
            chunks:
                shard0000    1
            { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)   #可以看到這裡片鍵的寫入,都是寫在shard0000裡面的。

#在寫期間的分片基本信息:
mongos> sh.status()
--- Sharding Status --- 
...
...
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
    {  "_id" : "dba",  "partitioned" : true,  "primary" : "shard0000" }
        dba.account
            shard key: { "name" : 1 }
            chunks:          #數據塊分佈
                shard0000    1
                shard0001    1
                shard0002    1
            { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) 
            { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 0) 
            { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1)   #可以看到片鍵寫入的基本分佈

#在寫完成後的基本信息:
mongos> sh.status()
--- Sharding Status --- 
...
...
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
    {  "_id" : "dba",  "partitioned" : true,  "primary" : "shard0000" }
        dba.account
            shard key: { "name" : 1 }
            chunks:          #數據塊分佈
                shard0000    2
                shard0001    1
                shard0002    2
            { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) 
            { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(4, 0) 
            { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) 
            { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) 
            { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1)  #最後片鍵寫入的分佈

上面加粗的信息對比上看到,本來在每個分片上都只有一個塊,最後在shard0000、shard0002上有2個塊,被拆分了。shard0001不變。這是因為mongos在收到寫請求的時候,會檢查當前塊的拆分閥值點。到達該閥值的時候,會向分片發起一個拆分的請求。例子中shard0000和shard0002里的塊被拆分了。分片內的數據進行了遷移(有一定的消耗),最後通過一個均衡器來對數據進行轉移分配。所以在寫入途中要是看到一個分片中集合的數量變小也是正常的。

balancer:  #均衡器
    Currently enabled:  yes
    Currently running:  yes   #正在轉移
        Balancer lock taken at Fri Jul 10 2015 22:57:27 GMT+0800 (CST) by mongo2:30000:1436540125:1804289383:Balancer:846930886

均衡器均衡器負責數據遷移,周期性的檢查分片是否存在不均衡,如果不存在則會開始塊的遷移,config.locks集合里的sta

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近寫的文章都是創業類,好吧,今天好好寫寫技術類的文章!畢竟這幾天在速成IOS,看的是object-c,由於速成的很快,好累!好在現在基本已經入了點門道了,這才看的懂新人的代碼,才能提前感受代碼危機。對於IOS的速成口訣,回頭再分享,今天先分享一個簡單的抽象封裝! ...
  • json介紹 JSON(JavaScript Object Notation, JS 對象標記) 是一種輕量級的數據交換格式。它基於 ECMAScript 規範的一個子集,採用完全獨立於編程語言的文本格式來存儲和表示數據。簡潔和清晰的層次結構使得 JSON 成為理想的數據交換語言。 易於人閱讀和編寫 ...
  • 1.new project -> Scrolling Activity 2.Layout xml code activity_scrolling.xml content_scrolling.xml 3.Activity code ...
  • 前面章節中的例子,用來作為單個節點的伺服器示例是足夠的,但是如果想要把它應用到生產環境,就遠遠不夠了。在Kafka中有很多參數可以控制它的運行和工作。大部分的選項都可以忽略直接使用預設值就好,遇到一些特殊的情況你可以再考慮使用它們。 本文翻譯自《Kafka權威指南》 Broker的一般配置 有很多參 ...
  • 這隻是一個簡單的排名例子,但可以從簡單的例子發現一些事物的某種共性,由此可以明白它可以應用於相識而不同的各種應用案例中。 ...
  • Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM Select CONVERT(varchar(100), GETDATE(), 1): 05/16/06 Select CONVERT(varchar(100), GETDA ...
  • 概述:UNION、SELECT INTO、INSERT INTO SELECT、SQL 約束。 UNION操作符 UNION 操作符用於合併兩個或多個 SELECT 語句的結果集。 請註意,UNION 內部的每個 SELECT 語句必須擁有相同數量的列。列也必須擁有相似的數據類型。同時,每個 SEL ...
  • http://blog.csdn.net/lzq123_1/article/details/51354179 註意:要將/usr/bin/mysql_install_db替換成 /usr/bin/mysql_install_db --datadir=usr/local/mysql/data/var ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...