当前位置:Gxlcms > 数据库问题 > MongoDB 分片的原理、搭建、应用

MongoDB 分片的原理、搭建、应用

时间:2021-07-01 10:21:17 帮助过:22人阅读

因为要放到后台用用配置文件启动,需要修改配置文件: 

/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)

上面加粗部分表示分片信息已经配置完成。

四、测试 : 对dba库的account集合进行测试,随机写入,查看是否分散到3个分片中。

通过一个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

所以要是遇到分片写入比单点 写入慢就是因为分片路由服务(mongos)需要维护元数据、数据迁移、路由开销等 。

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

五、高可用:Sharding+Replset

上面的分片都是单点的,要是一个分片坏了,则数据会丢失,利用之前减少的副本集,能否把副本集加入到分片中?下面就来说明下。

1)添加副本集分片服务器(mmm副本集名称):这里测试就只对一个分片加副本集,要实现完全的高可用就需要对所有分片加副本集,避免单点故障

一个普通的副本集:

技术分享图片
mmm:PRIMARY> rs.status()
{
	"set" : "mmm",
	"date" : ISODate("2015-07-10T16:17:19Z"),
	"myState" : 1,
	"members" : [
		{
			"_id" : 2,
			"name" : "192.168.200.245:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 418,
			"optime" : Timestamp(1436545003, 1),
			"optimeDate" : ISODate("2015-07-10T16:16:43Z"),
			"lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),
			"lastHeartbeatRecv" : ISODate("2015-07-10T16:17:18Z"),
			"pingMs" : 0,
			"syncingTo" : "192.168.200.25:27017"
		},
		{
			"_id" : 3,
			"name" : "192.168.200.25:27017",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 891321,
			"optime" : Timestamp(1436545003, 1),
			"optimeDate" : ISODate("2015-07-10T16:16:43Z"),
			"self" : true
		},
		{
			"_id" : 4,
			"name" : "192.168.200.245:37017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 36,
			"optime" : Timestamp(1436545003, 1),
			"optimeDate" : ISODate("2015-07-10T16:16:43Z"),
			"lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),
			"lastHeartbeatRecv" : ISODate("2015-07-10T16:17:17Z"),
			"pingMs" : 0,
			"syncingTo" : "192.168.200.25:27017"
		}
	],
	"ok" : 1
}
View Code

现在需要把这个副本集加入到分片中:

mongos> sh.addShard("mmm/192.168.200.25:27017,192.168.200.245:27017,192.168.200.245:37017") #加入副本集分片
{ "shardAdded" : "mmm", "ok" : 1 }
mongos> sh.status()
--- Sharding Status --- 
...
...
shards: { "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017" } { "_id" : "shard0000", "host" : "192.168.200.51:40000" } { "_id" : "shard0001", "host" : "192.168.200.52:40000" } { "_id" : "shard0002", "host" : "192.168.200.53:40000" } balancer: Currently enabled: yes Currently running: no Failed balancer rounds in last 5 attempts: 0 Migration Results for the last 24 hours: 4 : Success databases: { "_id" : "admin", "partitioned" : false, "primary" : "config" } { "_id" : "test", "partitioned" : false, "primary" : "shard0000" } { "

人气教程排行