一、概念:
分片(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端口的配置服務器。

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
查看是否分片: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