一、概念:

分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。通过一个名为mongos的路由进程进行操作,mongos知道数据和片的对应关系(通过配置服务器)。大部分使用场景都是解决磁盘空间的问题,对于写入有可能会变差(+++里面的说明+++),查询则尽量避免跨分片查询。使用分片的时机:

  1. 1,机器的磁盘不够用了。使用分片解决磁盘空间的问题。
  2. 2,单个mongod已经不能满足写数据的性能要求。通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。
  3. 3,想把大量数据放到内存里提高性能。和上面一样,通过分片使用分片服务器自身的资源。

二、部署安装: 前提是安装了mongodb(本文用3.0测试)

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

  1. 配置服务器。是一个独立的mongod进程,保存集群和分片的元数据,即各分片包含了哪些数据的信息。最先开始建立,启用日志功能。像启动普通的mongod一样启动配置服务器,指定configsvr选项。不需要太多的空间和资源,配置服务器的1KB空间相当于真是数据的200MB。保存的只是数据的分布表。当服务不可用,则变成只读,无法分块、迁移数据。
  2. 路由服务器。即mongos,起到一个路由的功能,供程序连接。本身不保存数据,在启动时从配置服务器加载集群信息,开启mongos进程需要知道配置服务器的地址,指定configdb选项。
  3. 分片服务器。是一个独立普通的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个则会报错

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

同理再起一个22000端口的配置服务器。

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

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

  1. # mongos
  2.  
  3. #日志文件
  4. logpath=/var/log/mongodb/mongodb_route.log
  5. #日志追加
  6. logappend=true
  7. #端口
  8. port = 30000
  9. #最大连接数
  10. maxConns = 100
  11. #绑定地址
  12. #bind_ip=192.168.200.*,...,
  13.  
  14. pidfilepath = /var/run/mongo_30000.pid
  15.  
  16. configdb=192.168.200.A:20000,192.168.200.A:21000,192.168.200.A:22000 #必须是1个或则3个配置 。
  1. #configdb=127.0.0.1:20000 #报错
  1. #守护进程模式 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
  2. root 9020 1 0 14:47 ? 00:00:06 mongod -f /etc/mongod_40000.conf
  3. root 9990 1 0 15:14 ? 00:00:02 mongod -f /etc/mongod_20000.conf
  4. root 10004 1 0 15:14 ? 00:00:01 mongod -f /etc/mongod_21000.conf
  5. root 10076 1 0 15:20 ? 00:00:00 mongod -f /etc/mongod_22000.conf
  6. root 10096 1 0 15:20 ? 00:00:00 mongos -f /etc/mongod_30000.conf

按照上面的方法再到B上开启分片服务和路由服务(配置文件一样),以及在C上开启分片服务。到此分片的配置服务器、路由服务器、分片服务器都已经部署完成。

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

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

登陆路由服务器mongos 操作:

  1. root@mongo1:~# mongo --port=30000
  2. MongoDB shell version: 3.0.4
  3. connecting to: 127.0.0.1:30000/test
  4. 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.  
  19. mongos> sh.addShard("192.168.200.A:40000") #添加分片
  20. { "shardAdded" : "shard0000", "ok" : 1 }
  21. mongos> sh.addShard("192.168.200.B:40000") #添加分片
  22. { "shardAdded" : "shard0001", "ok" : 1 }
  23. mongos> sh.addShard("192.168.200.C:40000") #添加分片
  24. { "shardAdded" : "shard0002", "ok" : 1 }
  25.  
  26. mongos> sh.status() #查看集群信息
  27. --- Sharding Status ---
  28. sharding version: {
  29. "_id" : 1,
  30. "minCompatibleVersion" : 5,
  31. "currentVersion" : 6,
  32. "clusterId" : ObjectId("559f72470f93270ba60b26c6")
  33. }
  34. shards: #分片信息
  35. { "_id" : "shard0000", "host" : "192.168.200.A:40000" }
  36. { "_id" : "shard0001", "host" : "192.168.200.B:40000" }
  37. { "_id" : "shard0002", "host" : "192.168.200.C:40000" }
  38. balancer:
  39. Currently enabled: yes
  40. Currently running: no
  41. Failed balancer rounds in last 5 attempts: 0
  42. Migration Results for the last 24 hours:
  43. No recent migrations
  44. databases:
  45. { "_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. databases:
  6. { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  7. { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  8. { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  9.  
  10. mongos> sh.shardCollection("dba.account",{"name":}) #再对集合进行分片,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)

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

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

想要看到详细的信息则需要执行:

  1. mongos> sh.status({"verbose":1})
  2. 或则
  3. mongos> db.printShardingStatus("vvvv")
  4. 或则
  5. mongos> printShardingStatus(db.getSisterDB("config"),1)

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

判断是否为shard:db.runCommand({isdbgrid:1})

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

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

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

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

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

上面加粗部分为集合的基本信息,可以看到分片成功,各个分片都有数据(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, ) #可以看到这里片键的写入,都是写在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, ) #可以看到片键写入的基本分布

  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:

均衡器:均衡器负责数据迁移,周期性的检查分片是否存在不均衡,如果不存在则会开始块的迁移,config.locks集合里的state表示均衡器是否找正在运行,0表示非活动状态,2表示正在均衡。均衡迁移数据的过程会增加系统的负载:目标分片必须查询源分片的所有文档,将文档插入目标分片中,再清除源分片的数据。可以关闭均衡器(不建议):关闭会导致各分片数据分布不均衡,磁盘空间得不到有效的利用。

  1. mongos> sh.setBalancerState(false) #关闭自动均衡器,手动均衡,打开:sh.setBalancerState(true)
  2. mongos> db.settings.find() #查看均衡器状态
  3. { "_id" : "balancer", "stopped" : true }

可以为均衡器设置一个均衡时间窗口:activeWindow

  1. mongos> db.settings.update({"_id":"balancer"},{"$set":{"activeWindow":{"start":"08:00","stop":"02:00"}}},true)
  2. WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  3. mongos> db.settings.find({"_id":"balancer"})
  4. { "_id" : "balancer", "stopped" : false, "activeWindow" : { "start" : "08:00", "stop" : "02:00" } }

上面说明:均衡只会在早上8点到凌晨2点进行均衡操作。均衡器是以块的数量作为迁移指标,而非数据大小,块的大小默认是64M,可以修改:(config.settings)

  1. mongos> db.settings.find()
  2. { "_id" : "chunksize", "value" : 64 }
  3. mongos> db.settings.save({"_id":"chunksize","value":})
  4. WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  5. mongos> db.settings.find()
  6. { "_id" : "chunksize", "value" : 32 }

上面把块的默认大小改成了32M,除了通过均衡器自动迁移外,还可以手动迁移数据sh.moveChunk("db.collection",{块地址},"新片名称")

  1. mongos> db.chunks.find({"_id" : "abc.account-name_\"wPeFnJEvendSTbH\""}).pretty() #先到config.chunks上任意找一个块
  2. {
  3. "_id" : "abc.account-name_\"wPeFnJEvendSTbH\"",
  4. "lastmod" : Timestamp(3, 1),
  5. "lastmodEpoch" : ObjectId("55a52ff1fdd9a605a0371327"),
  6. "ns" : "abc.account",
  7. "min" : {
  8. "name" : "wPeFnJEvendSTbH" #被移动的块
  9. },
  10. "max" : {
  11. "name" : { "$maxKey" : 1 }
  12. },
  13. "shard" : "shard0000" #原先所在的分片
  14. }
  15. mongos> sh.moveChunk("abc.account",{"name" : "wPeFnJEvendSTbH"},"mablevi") #把abc.account集合中包含name(片键)为""的快迁移到mablevi分片中
  16. { "millis" : 6800, "ok" : }
  17. mongos> db.chunks.find({"_id" : "abc.account-name_\"wPeFnJEvendSTbH\""}).pretty()
  18. {
  19. "_id" : "abc.account-name_\"wPeFnJEvendSTbH\"",
  20. "lastmod" : Timestamp(5, 0),
  21. "lastmodEpoch" : ObjectId("55a52ff1fdd9a605a0371327"),
  22. "ns" : "abc.account",
  23. "min" : {
  24. "name" : "wPeFnJEvendSTbH"
  25. },
  26. "max" : {
  27. "name" : { "$maxKey" : 1 }
  28. },
  29. "shard" : "mablevi" #已被迁移到新片
  30. }

上面是手动移动数据的操作,数据被移动。 要是块超出了64M限制【原因是片键没选好(日期、状态值等),导致一个块无限增大】,则无法进行自动均衡,无法分块。有2个办法:1是加大块的大小(setting),2是拆分sh.splitAt()(推荐)。

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

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

五、高可用:Sharding+Replset

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

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

一个普通的副本集:

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

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

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

把之前的副本集分片删除了,如何删除见下面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.  
  4. mongos> sh.status()
  5. --- Sharding Status ---
  6. ...
  7. ...
  8. shards:
  9. { "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }
  10. { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  11. { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  12. { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  13. ...
  14. ...
  15. dba.account
  16. shard key: { "name" : 1 }
  17. chunks:
  18. mablevi 1
  19. shard0000 1
  20. shard0001 1
  21. shard0002 2
  22. { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  23. { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : mablevi Timestamp(9, ) #新加入的分片得到数据
  24. { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1)
  25. { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  26. { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(9, 1)
  27. { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  28. { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }

继续用python写入操作:

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

副本集的分片被写入了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. shards:
  13. { "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017", "draining" : true } #删除的分片数据移动到其他分片
  14. { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  15. { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  16. { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  17. ...
    ...
  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. shard0000 2
  26. shard0001 1
  27. shard0002 2
  28. { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  29. { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0)
  30. { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #这里已经没有了被删除分片信息
  31. { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  32. { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1)
  33. { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  34. { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }
  35.  
  36. mongos> db.runCommand({"removeshard":"mmm"}) #再次执行,直到执行成功,要是原来分片的数据比较大,这里比较费时,要是一个主分片则需要执行movePrimary
  37. {
  38. "msg" : "removeshard completed successfully",
  39. "state" : "completed", #完成删除
  40. "shard" : "mmm",
  41. "ok" : 1
  42. }
  43. mongos> sh.status()
  44. --- Sharding Status ---...
  45. shards: #分片消失
  46. { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  47. { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  48. { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  49. ...
    ...
  50. { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0)
  51. { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0)
  52. { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #已经没有了被删除分片的信息
  53. { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4)
  54. { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1)
  55. { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }
  56. { "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }

分片被删除之后,数据被移到其他分片中,不会丢失。要是想让主分片进行转移则(movePrimary):

  1. mongos> db.adminCommand({"movePrimary":"test","to":"shard0001"}) #把test的主分片从shard0000迁移到shard0001 

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

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

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

  1. mongos> use admin #需要进入admin才能执行
  2. switched to db admin
  3. mongos> db.runCommand({ listshards : })
  4. {
  5. "shards" : [
  6. {
  7. "_id" : "shard0000",
  8. "host" : "192.168.200.51:40000"
  9. },
  10. {
  11. "_id" : "shard0001",
  12. "host" : "192.168.200.52:40000"
  13. },
  14. {
  15. "_id" : "shard0002",
  16. "host" : "192.168.200.53:40000"
  17. },
  18. {
  19. "_id" : "mablevi",
  20. "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002"
  21. }
  22. ],
  23. "ok" : 1
  24. }

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

六、认证分配

上面的所有操作都是在无账号密码下进行的,这样是不安全的,那如何使用账号密码呢?和副本级一样,需要添加KeyFile参数,但是针对上面的三个角色(config、mongos、mongod)账号密码怎么添加呢?官网上已经做了说明:http://docs.mongodb.org/manual/tutorial/enable-authentication-in-sharded-cluster/。下面就对有账号密码认证分片进行相关设置说明

首先要创建账号(Root角色)和生成一个KeyFile文件其中mongos 不需要创建账号。

  1. openssl rand -base64 741 > mongodb-keyfile
  2. chmod 600 mongodb-keyfile

其实这个文件也可以直接用明文,只要保证各个地方指定的文件是同一个就可以了。

1)mongd: 首先在mongod角色的分片成员上生成key file文件,特别注意的是有副本级的分片,再把这个文件分别复制到其他角色的服务器上。再添加参数:

  1. auth = true
  2. keyFile = /usr/local/mongodb-keyfile

2)Config上添加参数:

  1. auth = true
  2. keyFile = /usr/local/mongodb-keyfile

3)mongos上添加参数,因为mongos本来就是从config里加载数据的,所以只需要添加keyfile文件即可,不需要找上面createUser。

  1. keyFile = /usr/local/mongodb-keyfile

最后重启各个服务,再进入mongos里查看:

  1. root@mongo1:/usr/local# mongo --port=30000
  2. MongoDB shell version: 3.0.4
  3. connecting to: 127.0.0.1:30000/test
  4. mongos> sh.status() #没有认证,没有权限报错。
  5. 2015-07-14T23:42:11.800+0800 E QUERY Error: error: { "$err" : "not authorized for query on config.version", "code" : 13 }
  6. at Error (<anonymous>)
  7. at DBQuery.next (src/mongo/shell/query.js:259:15)
  8. at DBCollection.findOne (src/mongo/shell/collection.js:189:22)
  9. at printShardingStatus (src/mongo/shell/shardingtest.js:659:55)
  10. at Function.sh.status (src/mongo/shell/utils_sh.js:60:5)
  11. at (shell):1:4 at src/mongo/shell/query.js:259
  12. mongos> use admin
  13. switched to db admin
  14. mongos> db.auth('dba','dba') #认证
  15. 1
  16. mongos> sh.status() #有权限
  17. --- Sharding Status ---
  18. sharding version: {
  19. "_id" : 1,
  20. "minCompatibleVersion" : 5,
  21. "currentVersion" : 6,
  22. "clusterId" : ObjectId("55a51ef18bd517d4acec5ef9")
  23. }
  24. shards:
  25. { "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }
  26. { "_id" : "shard0000", "host" : "192.168.200.51:40000" }
  27. { "_id" : "shard0001", "host" : "192.168.200.52:40000" }
  28. { "_id" : "shard0002", "host" : "192.168.200.53:40000" }
  29. balancer:
    ...
    ...
  30. databases:
  31. { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  32. { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
  33. { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }
  34. dba.account
  35. shard key: { "name" : 1 }
  36. chunks:
  37. mablevi 1
  38. shard0000 1
  39. shard0001 2
  40. shard0002 1
  41. { "name" : { "$minKey" : 1 } } -->> { "name" : "9XXqCaBhfhPIXLq" } on : mablevi Timestamp(2, 0)
  42. { "name" : "9XXqCaBhfhPIXLq" } -->> { "name" : "RWINvgjYYQmbZds" } on : shard0002 Timestamp(4, 0)
  43. { "name" : "RWINvgjYYQmbZds" } -->> { "name" : "jSPRBNH8rvnzblG" } on : shard0001 Timestamp(4, 1)
  44. { "name" : "jSPRBNH8rvnzblG" } -->> { "name" : "okmjUUZuuKgftDC" } on : shard0001 Timestamp(3, 4)
  45. { "name" : "okmjUUZuuKgftDC" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1)

七、分片备份、还原

因为分片机制里面会有平衡器来迁移数据,所以各个分片里的数据很可能会移动,所以在备份分片时需要做:

①:先停止平衡器的工作,并检查没有chunk move动作,保证dump的时候没有进行数据迁移。

  1. mongos> sh.stopBalancer()

②:锁定数据库,保证数据没有写入:在各个分片上和配置服务器上执行。

  1. > db.fsyncLock()
  2. {
  3. "info" : "now locked against writes, use db.fsyncUnlock() to unlock",
  4. "seeAlso" : "http://dochub.mongodb.org/core/fsynccommand",
  5. "ok" : 1
  6. }

③:执行备份操作,备份各个分片服务器和配置服务器。

  1. mongodump -udba -p12345 -d dba_test --authenticationDatabase admin -o backup/

④:解锁数据库,备份完成之后在分片和配置服务器上解锁数据库,允许修改。

  1. > db.fsyncUnlock()
  2. { "ok" : 1, "info" : "unlock completed" }

当数据库出现问题,需要还原的时候,需要还原各个分片和配置服务器,并且重启MongoDB实例。还原数据库需要做:

①:还原各个分片和配置服务器。

  1. mongorestore --host=127.0.0.1 --port=27017 -udba -p12345 -d dba_test --authenticationDatabase admin --drop backup/dba_test

②:重启各个实例

总结:

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

https://www.cnblogs.com/zhoujinyi/p/4635444.html

参考文档:

说明:http://docs.mongodb.org/manual/core/sharding-introduction/

配置:http://docs.mongodb.org/manual/tutorial/deploy-shard-cluster/

应用:http://www.caiyiting.com/blog/2014/replica-sets-sharding-realization.html

MongoDB 分片的原理、搭建、应用 (转)的更多相关文章

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

    一.概念: 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载.基本思想就是将集合切成小块,这 ...

  2. MongoDB 分片的原理、搭建、应用 !

    MongoDB 分片的原理.搭建.应用   一.概念: 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处 ...

  3. MongoDB分片技术原理和高可用集群配置方案

    一.Sharding分片技术 1.分片概述 当数据量比较大的时候,我们需要把数分片运行在不同的机器中,以降低CPU.内存和Io的压力,Sharding就是数据库分片技术. MongoDB分片技术类似M ...

  4. MongoDB分片集群搭建及扩容

    ### 实验:分片集群搭建及扩容#### 实验目标及流程 * 目标:学习如何搭建一个两分片的分片集群 * 环境:3台Linux虚拟机器,4Core 8GB * 步骤: * 配置域名解析 * 准备分片目 ...

  5. MongoDB 分片集群搭建

    一.概述 分片是一种在多台机器上分配数据的方法.MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作.有两种解决系统增长的方法:垂直扩展和水平扩展. 垂直扩展涉及增加单个服务器的容量,例如使用 ...

  6. Mongodb 分片原理

    1.主从mongodb 模式 类似,MySQL的主从配置  参照:https://blog.csdn.net/liusong0605/article/details/11551699 mongoDB有 ...

  7. MongoDB 分片管理(不定时更新)

    背景: 通过上一篇的 MongoDB 分片的原理.搭建.应用 大致了解了MongoDB分片的安装和一些基本的使用情况,现在来说明下如何管理和优化MongoDB分片的使用. 知识点: 1) 分片的配置和 ...

  8. MongoDB 分片集群实战

    背景 在如今的互联网环境下,海量数据已随处可见并且还在不断增长,对于如何存储处理海量数据,比较常见的方法有两种: 垂直扩展:通过增加单台服务器的配置,例如使用更强悍的 CPU.更大的内存.更大容量的磁 ...

  9. 分布式文档存储数据库之MongoDB分片集群

    前文我们聊到了mongodb的副本集以及配置副本集,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13953598.html:今天我们来聊下mongodb的分片 ...

随机推荐

  1. advanceInstaller制作中文安装界面

    AdvanceInstaller15.7 ,上述画框选择.

  2. 5中I/O模型

    输入操作包括两个阶段1.等待网络数据到达,被复制到内核中的缓冲区2.从内核缓冲区复制到进程缓冲区5种I/O模型1.阻塞式I/O:包含数据被复制到内核缓冲区和应用进程缓冲区两个过程,调用recvfrom ...

  3. DevExpress XtraReport - 动态加载报表布局模板

    XtraReport的报表模板文件是.repx,下面的代码演示动态加载报表布局模板. XtraReport mReport = new XtraReport(); mReport.LoadLayout ...

  4. nginx deny 封IP

    官方文档地址:http://nginx.org/en/docs/http/ngx_http_access_module.html#deny Syntax: deny address | CIDR | ...

  5. [ARM-Linux开发]linux dmesg命令参数及用法详解(linux显示开机信息命令)

    功能说明:显示开机信息.语 法:dmesg [-cn][-s <缓冲区大小>]补充说明:kernel会将开机信息存储在ring buffer中.您若是开机时来不及查看信息,可利用dmesg ...

  6. centos7 spark2.3.1集群搭建

    1.安装jdk 2.安装scala 参照jdk的安装 3.ssh 免密码登录 4.安装hadoop 以上四步请参照   centos7 安装hadoop2.7.6(分布式) 5.安装spark  1) ...

  7. 使用Docker Compose搭建Service Mesh

    使用Docker Compose搭建Service Mesh 本文将介绍如何使用Docker Compose搭建Istio.Istio号称支持多种平台(不仅仅Kubernetes).然而,官网上非基于 ...

  8. 【ztree】隐藏显示节点

    //显示隐藏的节点 var nodes = zTreeObj.getNodesByParam("isHidden", true); zTreeObj.showNodes(nodes ...

  9. php数组到json的转变

    今天做项目遇到个问题,一个接口,输出二维数组,前端说他要的数据格式是数组,而不是对象,就像上个数据一样,我当时就懵逼了,,,什么对象?我明明输出的是数组啊...然后我看了看我返回的json串,emmm ...

  10. 虚拟机CentOS创建/使用快照

    快照 1.什么是快照 说的直白一点,就是创建一个备份.当执行了不可逆的错误操作后,可以通过快照用来恢复系统 2.创建快照的三种模式 挂载状态下创建快照 开机状态下创建快照 关机状态下创建快照 3.如何 ...