主从模式可以用于备份,故障恢复,读扩展等,可以配置一个主节点一个从节点或者多个从节点;
主节点只有一个
我们在一台服务器上实现主从模式
master.conf
dbpath=I:\mongodb\masterdata port=8888 #端口号 默认为27017 bind_ip=127.0.0.1 master=trueslave.conf 配置 dbpath=I:\mongodb\slavedata port=9999 #端口号 默认为27017 bind_ip=127.0.0.1 source=127.0.0.1:8888 slave=true启动:主节点-〉从节点mongod --config I:\mongodb\master.conf
mongod --config I:\mongodb\slave.conf
最后启动两个终端连接
master操作:
> use db1; switched to db db1 > function add(){ ... var i=0; ... for (;i<50;i++){ ... db.c1.insert({"name":"doc"+i}) ... } ... } > add() > db.c1.findOne() { "_id" : ObjectId("5986d80d25fe8e60ed262340"), "name" : "doc0" } > db.createUser({user:'admin1',pwd:'admin1',roles:[{role:"root",db:"admin"}]}) Successfully added user: { "user" : "admin1", "roles" : [ { "role" : "root", "db" : "admin" } ] } > db.auth('admin1','admin1') 1slave操作 > show dbs; 2017-08-06T16:59:32.179+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1 shellHelper.show@src/mongo/shell/utils.js:769:19 shellHelper@src/mongo/shell/utils.js:659:15 @(shellhelp2):1:1 > db.createUser({user:'admin1',pwd:'admin1',roles:[{role:"root",db:"admin"}]}) 2017-08-06T16:59:38.354+0800 E QUERY [thread1] Error: couldn't add user: not master : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype.createUser@src/mongo/shell/db.js:1290:15 @(shell):1:1 > rs.slaveOk() > show dbs; admin 0.000GB db1 0.000GB local 0.000GB > use db1; switched to db db1 > db.c1.findOne() { "_id" : ObjectId("5986d80d25fe8e60ed262340"), "name" : "doc0" }其中下边的命令如果不使用,默认slave是禁止读写的 rs.slaveOk()在主从结构中,主节点的操作记录成为oplog。oplog存储在一个系统数据库local的集合oplog.$main中,这个集合的每个文档都代表主节点上执行的一个操作。 从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作! 主从复制的其他设置项 –only 从节点指定复制某个数据库,默认是复制全部数据库 –slavedelay 从节点设置主数据库同步数据的延迟(单位是秒) –fastsync 从节点以主数据库的节点快照为节点启动从数据库 –autoresync 从节点如果不同步则从新同步数据库(即选择当通过热添加了一台从服务器之后,从服务器选择是否更新主服务器之间的数据) –oplogSize 主节点设置oplog的大小(主节点操作记录存储到local的oplog中)
> use local switched to db local > db.sources.find() > show tables; me oplog.$main startup_log > show collections; me oplog.$main startup_log从节点,存在一个集合sources。这个集合就保存了我这个服务器的主服务器是谁 > use local switched to db local > show tables; me sources startup_log > db.sources.find() { "_id" : ObjectId("5986d64779fd5d5f97e126c0"), "host" : "127.0.0.1:8888", "sour ce" : "main", "syncedTo" : Timestamp(1502010374, 1) }可以在从节点上动态操作主从关系,挂载到某主节点下 db.sources.insert({“host”:”127.0.0.1:8888”})
删除挂在的主节点
db.sources.remove({“host”:”127.0.0.1:8888”})
副本集类似主从复制,但是有些区别,主要区别是:主从复制在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点,不会出现宕机的情况。
副本集集群中没有特定的主数据库;如果哪个主数据库,集群中就会推选出一个从属数据库作为主数据库顶上,这就具备了自动故障恢复功能
副本及特征:
N 个节点的集群 任何节点可作为主节点 所有写入操作都在主节点上 自动故障转移 自动恢复
三个配置文件
C1.conf
dbpath=I:\mongodb\C1 port=1001 #端口号 bind_ip=127.0.0.1 #服务地址 replSet=rs1/127.0.0.1:1002 #设置同伴,rs1位集群名称 C2.conf dbpath=I:\mongodb\C2 port=1002 #端口号 bind_ip=127.0.0.1 replSet=rs1/127.0.0.1:1003 C3.conf dbpath=I:\mongodb\C3 port=1003 #端口号 bind_ip=127.0.0.1 replSet=rs1/127.0.0.1:1001 这个配置使得三台服务器组成一个闭环,依次启动三个服务器初始化副本集
> config = {_id: 'rs1', members: [{ ... "_id":1, ... "host":"127.0.0.1:1001" ... },{ ... "_id":2, ... "host":"127.0.0.1:1002" ... },{ ... "_id":3, ... "host":"127.0.0.1:1003" ... }] ... } { "_id" : "rs1", "members" : [ { "_id" : 1, "host" : "127.0.0.1:1001" }, { "_id" : 2, "host" : "127.0.0.1:1002" }, { "_id" : 3, "host" : "127.0.0.1:1003" } ] } > rs.initiate(config) { "ok" : 1 }可以使用rs.status(),来查看副本集状态后边看到初始化之后,命令行变为:rs1:PRIMARY>,如果重新启动连接第一个会变为:rs1:PRIMARY>,表示活动的节点,其他未备份节点;只有活跃节点才能进行查询数据库的信息操作,备份节点不能
在Mongodb里面存在另一种集群,类似于数据库表中的分区概念,就是分片技术,可以满足MongoDB数据量大量增长的需求;
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。我们可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据,这就是数据的水平扩展
使用分片的理由:
复制时所有的写入都是在主节点
副本集限制在12节点
请求量大时会出现内存不足
本地磁盘不足
垂直扩展成本高
官网说明:
shard: Each shard contains asubset of the sharded data. Each shard can be deployed as a replicaset.mongos: The mongos acts as aquery router, providing an interface between client applications and thesharded cluster.config servers: Configservers store metadata and configuration settings for the cluster. Asof MongoDB 3.4, config servers must be deployed as a replica set (CSRS).解释:
四个组件:mongos、config server、shard、replica set。 mongos:数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。 config server:顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货, mongodb集群就不会挂掉。 shard:分片,台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就可以了,为什么要分给四台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在mongodb集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片! replica set:前面已经详细讲过了这个东东,怎么这里又来凑热闹!其实上图4个分片如果没有 replica set 是个不完整架构,假设其中的一个分片挂掉那四分之一的数据就丢失了,所以在高可用性的分片架构还需要对于每一个分片构建 replica set 副本集保证分片的可靠性。生产环境通常是 2个副本 + 1个仲裁。 下面演示下如何搭建高可用的mongodb集群: 首先确定各个组件的数量,mongos 3个, config server 3个,数据分3片 shard server 3个,每个shard 有一个副本一个仲裁也就是 3 * 2 = 6 个,总共需要部署15个实例。这些实例可以部署在独立机器也可以部署在一台机器 本实例搭建在一个服务器上
搭建实例
下边的配置文件路径不再说明
1,规划5个组件对应的端口号,由于一个机器需要同时部署 mongos、config server 、shard1、shard2、shard3,所以需要用端口进行区分。 这个端口可以自由定义,在本文 mongos为 20000, config server 为 21000 , shard1为 22001 , shard2为22002, shard3为22003
2,启动三个配置服务器
C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data1 --port 21000 C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data2 --port 21001 C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data3 --port 21002连接任意一台配置服务器,创建配置服务器副本集 mongo --host 127.0.0.1 --port 21000 rs.initiate({_id:"cfgReplSet",configsvr:true,members:[{_id:0,host:"127.0.0.1:21000"},{_id:1,host:"127.0.0.1:21001"},{_id:2,host:"127.0.0.1:21002"}]}) C:\Users\Administrator>mongo --host 127.0.0.1 --port 21000 MongoDB shell version v3.4.5 connecting to: mongodb://127.0.0.1:21000/ MongoDB server version: 3.4.5 Server has startup warnings: 2017-08-12T17:14:32.377+0800 I CONTROL [initandlisten] 2017-08-12T17:14:32.379+0800 I CONTROL [initandlisten] ** WARNING: Access contr ol is not enabled for the database. 2017-08-12T17:14:32.381+0800 I CONTROL [initandlisten] ** Read and wri te access to data and configuration is unrestricted. 2017-08-12T17:14:32.383+0800 I CONTROL [initandlisten] > rs.initiate({_id:"cfgReplSet",configsvr:true,members:[{_id:0,host:"127.0.0.1:2 1000"},{_id:1,host:"127.0.0.1:21001"},{_id:2,host:"127.0.0.1:21002"}]})) { "ok" : 1 } cfgReplSet:OTHER>同样操作一次shard2,shard33,每一台服务器分别启动分片及副本集
mongod --shardsvr --replSet shard1ReplSet --port 22001 --dbpath I:\mongodb\shardtest\shard1data\d1 --nojournal mongod --shardsvr --replSet shard1ReplSet --port 22011 --dbpath I:\mongodb\shardtest\shard1data\d2 --nojournal mongod --shardsvr --replSet shard1ReplSet --port 22021 --dbpath I:\mongodb\shardtest\shard1data\d3 --nojournal连接任意一台分片服务器,创建副本集并初始化 mongo --host 127.0.0.1 --port 22001 rs.initiate({_id:"shard1ReplSet",members:[{_id:0,host:"127.0.0.1:22001"},{_id:1,host:"127.0.0.1:22011"},{_id:2,host:"127.0.0.1:22021"}]}) 比如: C:\Users\Administrator>mongo --host 127.0.0.1 --port 22001 MongoDB shell version v3.4.5 connecting to: mongodb://127.0.0.1:22001/ MongoDB server version: 3.4.5 Server has startup warnings: 2017-08-12T17:44:50.789+0800 I CONTROL [initandlisten] 2017-08-12T17:44:50.790+0800 I CONTROL [initandlisten] ** WARNING: Access contr ol is not enabled for the database. 2017-08-12T17:44:50.792+0800 I CONTROL [initandlisten] ** Read and wri te access to data and configuration is unrestricted. 2017-08-12T17:44:50.793+0800 I CONTROL [initandlisten] > rs.initiate({_id:"shard1ReplSet",members:[{_id:0,host:"127.0.0.1:22001"},{_id: 1,host:"127.0.0.1:22011"},{_id:2,host:"127.0.0.1:22021"}]}) { "ok" : 1 } shard1ReplSet:OTHER>另外两台: mongod --shardsvr --replSet shard2ReplSet --port 22002 --dbpath I:\mongodb\shardtest\shard2data\d1 --nojournal mongod --shardsvr --replSet shard2ReplSet --port 22012 --dbpath I:\mongodb\shardtest\shard2data\d2 --nojournal mongod --shardsvr --replSet shard2ReplSet --port 22022 --dbpath I:\mongodb\shardtest\shard2data\d3 --nojournal mongo --host 127.0.0.1 --port 22002 rs.initiate({_id:"shard2ReplSet",members:[{_id:0,host:"127.0.0.1:22002"},{_id:1,host:"127.0.0.1:22012"},{_id:2,host:"127.0.0.1:22022"}]}) mongod --shardsvr --replSet shard3ReplSet --port 22003 --dbpath I:\mongodb\shardtest\shard3data\d1 --nojournal mongod --shardsvr --replSet shard3ReplSet --port 22013 --dbpath I:\mongodb\shardtest\shard3data\d2 --nojournal mongod --shardsvr --replSet shard3ReplSet --port 22023 --dbpath I:\mongodb\shardtest\shard3data\d3 --nojournal mongo --host 127.0.0.1 --port 22003 rs.initiate({_id:"shard3ReplSet",members:[{_id:0,host:"127.0.0.1:22003"},{_id:1,host:"127.0.0.1:22013"},{_id:2,host:"127.0.0.1:22023"}]})选择一台服务器启动mongos路由服务mongos --configdb cfgReplSet/127.0.0.1:21000,127.0.0.1:21001,127.0.0.1:21002 --port 20000
登陆路由服务客户端 mongo --host 127.0.0.1 --port 20000 添加分片到集群 sh.addShard("shard1ReplSet/127.0.0.1:22001,127.0.0.1:22011,127.0.0.1:22021") 一次添加shard2 shard3 sh.addShard("shard2ReplSet/127.0.0.1:22002,127.0.0.1:22012,127.0.0.1:22022") sh.addShard("shard3ReplSet/127.0.0.1:22003,127.0.0.1:22013,127.0.0.1:22023")
enable sharding for a database:
mongos> sh.enableSharding("test") { "ok" : 1 }shard a collection: mongos> sh.shardCollection("test.Log",{id:1}) { "collectionsharded" : "test.Log", "ok" : 1 } 测试 mongos> use test switched to db test mongos> for (var i=1; i<=100000; i++){ ... db.Log.save({id:i,"msg":"msg"+i}); ... } WriteResult({ "nInserted" : 1 }) 查看状态 mongos> db.Log.stats() { "sharded" : true, "capped" : false, "ns" : "test.Log", "count" : 100000, "size" : 5188895, "storageSize" : 1679360, "totalIndexSize" : 2183168, "indexSizes" : { "_id_" : 942080, "id_1" : 1241088 }, "avgObjSize" : 51, "nindexes" : 2, "nchunks" : 3, "shards" : { "shard1ReplSet" : { "ns" : "test.Log", "size" : 5188895, "count" : 100000, "avgObjSize" : 51, 。。。如果想均匀分布在不同的分片上,可以使用sharding key的策略为hashed,重新插入数据 sh.shardCollection("test.Log",{id:"hashed"}),然后插入数据即可,就可以保持数据的均衡