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

一、概念:

分片 (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

点击(此处)折叠或打开

  1. #数据目录
  2. dbpath=/usr/local/config/
  3. #日志文件
  4. logpath=/var/log/mongodb/mongodb_config.log
  5. #日志追加
  6. logappend=true
  7. #端口
  8. port = 20000
  9. #最大连接数
  10. maxConns = 50
  11. pidfilepath = /var/run/mongo_20000.pid
  12. #日志,redo log
  13. journal = true
  14. #刷写提交机制
  15. journalCommitInterval = 200
  16. #守护进程模式
  17. fork = true
  18. #刷写数据到日志的频率
  19. syncdelay = 60
  20. #storageEngine = wiredTiger
  21. #操作日志,单位M
  22. oplogSize = 1000
  23. #命名空间的文件大小,默认16M,最大2G。
  24. nssize = 16
  25. noauth = true
  26. unixSocketPrefix = /tmp
  27. configsvr = true
/etc/mongod_21000.conf

点击(此处)折叠或打开

  1. 数据目录
  2. dbpath=/usr/local/config1/
  3. #日志文件
  4. logpath=/var/log/mongodb/mongodb_config1.log
  5. #日志追加
  6. logappend=true
  7. #端口
  8. port = 21000
  9. #最大连接数
  10. maxConns = 50
  11. pidfilepath = /var/run/mongo_21000.pid
  12. #日志,redo log
  13. journal = true
  14. #刷写提交机制
  15. journalCommitInterval = 200
  16. #守护进程模式
  17. fork = true
  18. #刷写数据到日志的频率
  19. syncdelay = 60
  20. #storageEngine = wiredTiger
  21. #操作日志,单位M
  22. oplogSize = 1000
  23. #命名空间的文件大小,默认16M,最大2G。
  24. nssize = 16
  25. noauth = true
  26. unixSocketPrefix = /tmp
  27. configsvr = true
开启配置服务器:

点击(此处)折叠或打开

  1. 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

  2. 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端口的配置服务器。

点击(此处)折叠或打开

  1. #数据目录
  2. dbpath=/usr/local/config2/
  3. #日志文件
  4. logpath=/var/log/mongodb/mongodb_config2.log
  5. #日志追加
  6. logappend=true
  7. #端口
  8. port = 22000
  9. #最大连接数
  10. maxConns = 50
  11. pidfilepath = /var/run/mongo_22000.pid
  12. #日志,redo log
  13. journal = true
  14. #刷写提交机制
  15. journalCommitInterval = 200
  16. #守护进程模式
  17. fork = true
  18. #刷写数据到日志的频率
  19. syncdelay = 60
  20. #storageEngine = wiredTiger
  21. #操作日志,单位M
  22. oplogSize = 1000
  23. #命名空间的文件大小,默认16M,最大2G。
  24. nssize = 16

  25. noauth = true
  26. unixSocketPrefix = /tmp

  27. configsvr = true


2)路由服务器的启动。(A、B上各开启1个,Port:30000)

路由服务器不保存数据,把日志记录一下即可。

点击(此处)折叠或打开

  1. # mongos

  2. #日志文件
  3. logpath=/var/log/mongodb/mongodb_route.log
  4. #日志追加
  5. logappend=true
  6. #端口
  7. port = 30000
  8. #最大连接数
  9. maxConns = 100
  10. #绑定地址
  11. #bind_ip=192.168.200.*,...,

  12. pidfilepath = /var/run/mongo_30000.pid

  13. configdb=192.168.200.A:20000,192.168.200.A:21000,192.168.200.A:22000 #必须是1个或则3个配置 。
  14. #configdb=127.0.0.1:20000 #报错
  15. #守护进程模式 fork = true

其中最重要的参数是

configdb,不能在其后面带的配置服务器的地址写成localhost或则127.0.0.1,需要设置成其他分片也能访问的地址,即192.168.200.A:20000/21000/22000。否则在addshard的时候会报错:

点击(此处)折叠或打开

  1. {
  2. "ok" : 0,
  3. "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"
  4. }

开启mongos:

点击(此处)折叠或打开

  1. root@mongo1:~# mongos -f /etc/mongod_30000.conf
  2. 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
  3. about to fork child process, waiting until server is ready for connections.
  4. forked process: 8965
  5. child process started successfully, parent exiting

3)分片服务器的启动:

就是一个普通的mongod进程:

点击(此处)折叠或打开

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

A服务器上面的服务开启完毕

点击(此处)折叠或打开

  1. 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>

添加分片:

点击(此处)折叠或打开

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

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

  24. mongos> sh.status() #查看集群信息
  25. --- Sharding Status ---
  26.   sharding version: {
  27.     "_id" : 1,
  28.     "minCompatibleVersion" : 5,
  29.     "currentVersion" : 6,
  30.     "clusterId" : ObjectId("559f72470f93270ba60b26c6")
  31. }
  32.   shards: #分片信息
  33.     { "_id" : "shard0000", "host" : "192.168.200.A:40000" }
  34.     { "_id" : "shard0001", "host" : "192.168.200.B:40000" }
  35.     { "_id" : "shard0002", "host" : "192.168.200.C:40000" }
  36.   balancer:
  37.     Currently enabled: yes
  38.     Currently running: no
  39.     Failed balancer rounds in last 5 attempts: 0
  40.     Migration Results for the last 24 hours:
  41.         No recent migrations
  42.   databases:
  43.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }

2)开启分片功能:sh.enableSharding("库名")、sh.shardCollection("库名.集合名",{"key":1})

点击(此处)折叠或打开

  1. mongos> sh.enableSharding("dba") #首先对数据库启用分片
  2. { "ok" : 1 }
  3. mongos> sh.status() #查看分片信息
  4. --- Sharding Status ---...
  5. ...
  6.   databases:
  7.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  8.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  9.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }

  10. mongos> sh.shardCollection("dba.account",{"name":1}) #再对集合进行分片,name字段是片键。
  11. { "collectionsharded" : "dba.account", "ok" : 1 }
  12. mongos> sh.status()
  13. --- Sharding Status ---...
  14.   shards:
  15.     { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  16.     { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  17.     { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  18. ...
  19.   databases:
  20.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  21.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  22.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" } #库
  23.         dba.account
  24.             shard key: { "name" : 1 } #集合
  25.             chunks:
  26.                 shard0000 1
  27.             { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)

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

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

通过一个python脚本进行随机写入:分别向A、B 2个mongos各写入10万条记录。


点击(此处)折叠或打开

  1. #!/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3. #随即写MongoDB Shard 测试
  4. import pymongo
  5. import time
  6. from random import Random
  7. def random_str(randomlength=8):
  8.     str = ''
  9.     chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  10.     length = len(chars) - 1
  11.     random = Random()
  12.     for i in range(randomlength):
  13.         str+=chars[random.randint(0, length)]
  14.         return str
  15. def inc_data(conn):
  16.     db = conn.dba
  17. #    db = conn.test
  18.     collection = db.account
  19.     for i in range(100000):
  20.         str = ''
  21.         chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  22.         length = len(chars) - 1
  23.         random = Random()
  24.         for i in range(15):
  25.             str+=chars[random.randint(0, length)]
  26.             string = str
  27.         collection.insert({"name" : string, "age" : 123+i, "address" : "hangzhou"+string})
  28. if __name__ =='__main__':
  29.     conn = pymongo.MongoClient(host='192.168.200.A/B',port=30000)
  30.     StartTime = time.time()
  31.     print "===============$inc==============="
  32.     print "StartTime : %s" %StartTime
  33.     inc_data(conn)
  34.     EndTime = time.time()
  35.     print "EndTime : %s" %EndTime
  36.     CostTime = round(EndTime-StartTime)
  37.     print "CostTime : %s" %CostTime
查看是否分片:db.collection.stats()

点击(此处)折叠或打开

  1. mongos> db.account.stats() #查看集合的分布情况
  2. ...
  3. ...
  4.     "shards" : {
  5.         "shard0000" : {
  6.             "ns" : "dba.account",
  7.             "count" : 89710,
  8.             "size" : 10047520,
  9. ...
  10. ...
  11.         "shard0001" : {
  12.             "ns" : "dba.account",
  13.             "count" : 19273,
  14.             "size" : 2158576,
  15. ...
  16. ...
  17.         "shard0002" : {
  18.             "ns" : "dba.account",
  19.             "count" : 91017,
  20.             "size" : 10193904,
  21. ...
  22. ...

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

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

感兴趣的同学可以看下面这个比较有趣的现象:

点击(此处)折叠或打开

  1. #在写之前分片的基本信息:
  2. mongos> sh.status()
  3. --- Sharding Status ---
  4. ...
  5. ...
  6.   databases:
  7.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  8.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  9.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  10.         dba.account
  11.             shard key: { "name" : 1 }
  12.             chunks:
  13.     shard0000 1
  14.             { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0) #可以看到这里片键的写入,都是写在shard0000里面的。

  15. #在写期间的分片基本信息:
  16. mongos> sh.status()
  17. --- Sharding Status ---
  18. ...
  19. ...
  20.   databases:
  21.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  22.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  23.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  24.         dba.account
  25.             shard key: { "name" : 1 }
  26.             chunks: #数据块分布
  27.     shard0000 1
  28.     shard0001 1
  29.     shard0002 1
  30.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  31.             { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 0)
  32.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1) #可以看到片键写入的基本分布

  33. #在写完成后的基本信息:
  34. mongos> sh.status()
  35. --- Sharding Status ---
  36. ...
  37. ...
  38.   databases:
  39.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  40.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  41.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  42.         dba.account
  43.             shard key: { "name" : 1 }
  44.             chunks: #数据块分布
  45.     shard0000 2
  46.     shard0001 1
  47.     shard0002 2
  48.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  49.             { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(4, 0)
  50.             { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1)
  51.             { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  52.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1) #最后片键写入的分布

上面加粗的信息对比上看到,本来在每个分片上都只有一个块,最后在shard0000、shard0002上有2个块,被拆分了。shard0001不变。 这是因为mongos在收到写请求的时候,会检查当前块的拆分阀值点。到达该阀值的时候,会向分片发起一个拆分的请求。 例子中shard0000和shard0002里的块被拆分了。分片内的数据进行了迁移(有一定的消耗),最后通过一个均衡器来对数据进行转移分配。所以在写入途中要是看到一个分片中集合的数量变小也是正常的。

点击(此处)折叠或打开

  1. balancer: #均衡器
  2.     Currently enabled: yes
  3.     Currently running: yes #正在转移
  4.         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副本集名称):这里测试就只对一个分片加副本集,要实现完全的高可用就需要对所有分片加副本集,避免单点故障

一个普通的副本集:

点击(此处)折叠或打开

  1. mmm:PRIMARY> rs.status()
  2. {
  3.     "set" : "mmm",
  4.     "date" : ISODate("2015-07-10T16:17:19Z"),
  5.     "myState" : 1,
  6.     "members" : [
  7.         {
  8.             "_id" : 2,
  9.             "name" : "192.168.200.245:27017",
  10.             "health" : 1,
  11.             "state" : 2,
  12.             "stateStr" : "SECONDARY",
  13.             "uptime" : 418,
  14.             "optime" : Timestamp(1436545003, 1),
  15.             "optimeDate" : ISODate("2015-07-10T16:16:43Z"),
  16.             "lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),
  17.             "lastHeartbeatRecv" : ISODate("2015-07-10T16:17:18Z"),
  18.             "pingMs" : 0,
  19.             "syncingTo" : "192.168.200.25:27017"
  20.         },
  21.         {
  22.             "_id" : 3,
  23.             "name" : "192.168.200.25:27017",
  24.             "health" : 1,
  25.             "state" : 1,
  26.             "stateStr" : "PRIMARY",
  27.             "uptime" : 891321,
  28.             "optime" : Timestamp(1436545003, 1),
  29.             "optimeDate" : ISODate("2015-07-10T16:16:43Z"),
  30.             "self" : true
  31.         },
  32.         {
  33.             "_id" : 4,
  34.             "name" : "192.168.200.245:37017",
  35.             "health" : 1,
  36.             "state" : 2,
  37.             "stateStr" : "SECONDARY",
  38.             "uptime" : 36,
  39.             "optime" : Timestamp(1436545003, 1),
  40.             "optimeDate" : ISODate("2015-07-10T16:16:43Z"),
  41.             "lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),
  42.             "lastHeartbeatRecv" : ISODate("2015-07-10T16:17:17Z"),
  43.             "pingMs" : 0,
  44.             "syncingTo" : "192.168.200.25:27017"
  45.         }
  46.     ],
  47.     "ok" : 1
  48. }

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

点击(此处)折叠或打开

  1. mongos> sh.addShard("mmm/192.168.200.25:27017,192.168.200.245:27017,192.168.200.245:37017") #加入副本集分片
  2. { "shardAdded" : "mmm", "ok" : 1 }
  3. mongos> sh.status()
  4. --- Sharding Status ---
  5. ...
  6. ...
  7. shards:
  8.     { "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017" }
  9.     { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  10.     { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  11.     { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  12.   balancer:
  13.     Currently enabled: yes
  14.     Currently running: no
  15.     Failed balancer rounds in last 5 attempts: 0
  16.     Migration Results for the last 24 hours:
  17.         4 : Success
  18.   databases:
  19.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  20.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  21.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  22.         dba.account
  23.             shard key: { "name" : 1 }
  24.             chunks:
  25.                 mmm    1
  26.                 shard0000    1
  27.                 shard0001    1
  28.                 shard0002    2
  29.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  30.             { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : mmm Timestamp(5, 0)
  31.             { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1)
  32.             { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  33.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(5, 1)
  34.     { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" } #未设置分片

上面加粗部分表示副本集分片已经成功加入,并且 新加入的分片会分到已有的分片数据 。

点击(此处)折叠或打开

  1. mongos> db.account.stats()
  2. ...
  3. ...
  4.     "shards" : {
  5.         "mmm" : {
  6.             "ns" : "dba.account",
  7.             "count" : 7723,        #后加入的分片得到了数据
  8.             "size" : 741408,
  9.             "avgObjSize" : 96,
  10.             "storageSize" : 2793472,
  11.             "numExtents" : 5,
  12.             "nindexes" : 2,
  13.             "lastExtentSize" : 2097152,
  14.             "paddingFactor" : 1,
  15.             "systemFlags" : 1,
  16.             "userFlags" : 0,
  17.             "totalIndexSize" : 719488,
  18.             "indexSizes" : {
  19.                 "_id_" : 343392,
  20.                 "name_1" : 376096
  21.             },
  22.             "ok" : 1
  23.         },
  24. ...
  25. ...

2)继续用python脚本写数据,填充到副本集中  

由于之前的副本集是比较老的版本(2.4),所以在写入副本集分片的时候报错:

点击(此处)折叠或打开

  1. mongos> db.account.insert({"name":"UavMbMlfsz1OFrz"})
  2. WriteResult({
  3.     "nInserted" : 0,
  4.     "writeError" : {
  5.         "code" : 83,
  6.         "errmsg" : "write results unavailable from 192.168.200.25:27017 :: caused by :: Location28563 cannot send batch write operation to server 192.168.200.25:27017 (192.168.200.25)"
  7.     }
  8. })

太混蛋了,错误提示不太人性化,搞了半天。所以说版本一致性还是很重要的。现在重新开了一个副本集:

点击(此处)折叠或打开

  1. mablevi:PRIMARY> rs.status()
  2. {
  3.     "set" : "mablevi",
  4.     "date" : ISODate("2015-07-10T18:22:36.761Z"),
  5.     "myState" : 1,
  6.     "members" : [
  7.         {
  8.             "_id" : 1,
  9.             "name" : "192.168.200.53:50000",
  10.             "health" : 1,
  11.             "state" : 1,
  12.             "stateStr" : "PRIMARY",
  13.             "uptime" : 820,
  14.             "optime" : Timestamp(1436552412, 213),
  15.             "optimeDate" : ISODate("2015-07-10T18:20:12Z"),
  16.             "electionTime" : Timestamp(1436551910, 1),
  17.             "electionDate" : ISODate("2015-07-10T18:11:50Z"),
  18.             "configVersion" : 2,
  19.             "self" : true
  20.         },
  21.         {
  22.             "_id" : 2,
  23.             "name" : "192.168.200.53:50001",
  24.             "health" : 1,
  25.             "state" : 2,
  26.             "stateStr" : "SECONDARY",
  27.             "uptime" : 650,
  28.             "optime" : Timestamp(1436552412, 213),
  29.             "optimeDate" : ISODate("2015-07-10T18:20:12Z"),
  30.             "lastHeartbeat" : ISODate("2015-07-10T18:22:36.737Z"),
  31.             "lastHeartbeatRecv" : ISODate("2015-07-10T18:22:36.551Z"),
  32.             "pingMs" : 0,
  33.             "syncingTo" : "192.168.200.53:50000",
  34.             "configVersion" : 2
  35.         },
  36.         {
  37.             "_id" : 3,
  38.             "name" : "192.168.200.53:50002",
  39.             "health" : 1,
  40.             "state" : 2,
  41.             "stateStr" : "SECONDARY",
  42.             "uptime" : 614,
  43.             "optime" : Timestamp(1436552412, 213),
  44.             "optimeDate" : ISODate("2015-07-10T18:20:12Z"),
  45.             "lastHeartbeat" : ISODate("2015-07-10T18:22:36.742Z"),
  46.             "lastHeartbeatRecv" : ISODate("2015-07-10T18:22:36.741Z"),
  47.             "pingMs" : 0,
  48.             "syncingTo" : "192.168.200.53:50001",
  49.             "configVersion" : 2
  50.         }
  51.     ],
  52.     "ok" : 1,
  53.     "$gleStats" : {
  54.         "lastOpTime" : Timestamp(1436551942, 1),
  55.         "electionId" : ObjectId("55a00ae6a08c789ce9e4b50d")
  56.     }
  57. }

把之前的副本集分片删除了,如何删除见下面3)。

新的副本集加入分片中:

点击(此处)折叠或打开

  1. mongos> sh.addShard("mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002")
  2. { "shardAdded" : "mablevi", "ok" : 1 }
  3. mongos> sh.status()
  4. --- Sharding Status ---
  5. ...
  6. ...
  7.   shards:
  8.     { "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }
  9.     { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  10.     { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  11.     { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  12. ...
  13. ...
  14.         dba.account
  15.             shard key: { "name" : 1 }
  16.             chunks:
  17.                 mablevi    1
  18.                 shard0000    1
  19.                 shard0001    1
  20.                 shard0002    2
  21.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  22.          { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : mablevi Timestamp(9, 0) #新加入的分片得到数据
  23.             { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1)
  24.             { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  25.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(9, 1)
  26.     { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  27.     { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }

继续用python写入操作:

点击(此处)折叠或打开

  1. mongos> db.account.stats()
  2. {
  3. ...
  4. ...
  5.     "shards" : {
  6.         "mablevi" : {
  7.             "ns" : "dba.account",
  8.             "count" : 47240,
  9.             "size" : 5290880,
  10. ...
  11. ...

副本集的分片被写入了47240条记录。此时把副本集分片的Primary shutdown掉,再查看:

点击(此处)折叠或打开

  1. mongos> db.account.stats()
  2. {
  3.     "sharded" : true,
  4.     "code" : 13639,
  5.     "ok" : 0,
  6.     "errmsg" : "exception: can't connect to new replica set master [192.168.200.53:50000], err: couldn't connect to server 192.168.200.53:50000 (192.168.200.53), connection attempt failed" #由于副本集的Primary被shutdown之后,选举新主还是要几秒的时间,期间数据不能访问,导致分片数据也不能访问
  7. }
  8. mongos> db.account.stats()
  9. ...
  10. ...
  11.     "shards" : {
  12.         "mablevi" : {
  13.             "ns" : "dba.account",
  14.             "count" : 47240,     #副本集新主选举完毕之后,分片数据访问正常。数据没有丢失,高可用得到了实现。
  15.             "size" : 5290880,
  16. ...
  17. ...

要是让副本集分片只剩下一台(Secondary),则分片会报错 : 

点击(此处)折叠或打开

  1. mongos> db.account.stats()
  2. {
  3.     "sharded" : true,
  4.     "code" : 10009,
  5.     "ok" : 0,
  6.     "errmsg" : "exception: ReplicaSetMonitor no master found for set: mablevi" #数据不能访问
  7. }

3)删除分片: db.runCommand({"removeshard":"mmm"})

要是觉得分片太多了,想删除,则:

点击(此处)折叠或打开

  1. mongos> use admin #需要到admin下面删除
  2. switched to db admin
  3. mongos> db.runCommand({"removeshard":"mmm"})
  4. {
  5.     "msg" : "draining started successfully",
  6.     "state" : "started", #开始删除,数据正在转移
  7.     "shard" : "mmm",
  8.     "ok" : 1
  9. }
  10. mongos> sh.status()
  11. --- Sharding Status ---...
  12. ...
  13.   shards:
  14.     { "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017", "draining" : true } #删除的分片数据移动到其他分片
  15.     { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  16.     { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  17.     { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  18. ...
  19. ...
  20.   databases:
  21.     { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  22.     { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  23.     { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  24.         dba.account
  25.             shard key: { "name" : 1 }
  26.             chunks:
  27.                 shard0000    2
  28.                 shard0001    1
  29.                 shard0002    2
  30.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  31.             { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0)
  32.             { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #这里已经没有了被删除分片信息
  33.             { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  34.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1)
  35.     { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  36.     { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }
  37. mongos> db.runCommand({"removeshard":"mmm"}) #再次执行,直到执行成功,要是原来分片的数据比较大,这里比较费时。
  38. {
  39.     "msg" : "removeshard completed successfully",
  40.     "state" : "completed", #完成删除
  41.     "shard" : "mmm",
  42.     "ok" : 1
  43. }
  44. mongos> sh.status()
  45. --- Sharding Status ---...
  46.   shards: #分片消失
  47.     { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  48.     { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  49.     { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  50. ...
  51. ...
  52.             { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  53.             { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0)
  54.             { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #已经没有了被删除分片的信息
  55.             { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  56.             { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1)
  57.     { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  58.     { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }

分片被删除之后,数据被移到其他分片中,不会丢失。 

刷新下配置服务器:db.adminCommand({"flushRouterConfig":1})

db.adminCommand({"flushRouterConfig":1})

最后来查看下分片成员:db.runCommand({ listshards : 1 })

mongos> use admin  #需要进入admin才能执行 switched to db admin
mongos> db.runCommand({ listshards : 1 }) { "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" },
		{ "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }
	], "ok" : 1 }

到此已经把MongoDB分片原理、搭建、应用大致已经介绍完。

六、总结:

分片很好的解决了单台服务器磁盘空间、内存、cpu等硬件资源的限制问题,把数据水平拆分出去,降低单节点的访问压力。每个分片都是一个独立的数据库,所有的分片组合起来构成一个逻辑上的完整的数据库。因此,分片机制降低了每个分片的数据操作量及需要存储的数据量,达到多台服务器来应对不断增加的负载和数据的效果。后面文章还会继续对分片的其他方面进行说明介绍。


posted @ 2017-04-18 14:17  foreverfriends  阅读(245)  评论(0编辑  收藏  举报