1 mongoDB副本集

1 副本集简介

  前边我们介绍都是单机MongoDB的使用,在实际开发中很少会用单机MongoDB,因为使用单机会有数据丢失的风险,同时单台服务器无法做到高可用性(即当服务器宕机时,没有替代的服务器顶上来,我们的业务也就挂了),MongoDB中的副本集可以完美地解决上边的两个问题。

  MongoDB的副本集本质上就是一组mongod进程。复制集的成员有:
    1.Primary:主节点,负责所有的写操作;
    2.Secondaries:从节点,同步主节点的数据,保存数据副本;
    3.Arbiter:仲裁节点,不保存数据,只有一个投票的作用;
  副本集运行过程:主节点是集群中唯一一个负责写操作的节点,主节点的写操作都会记录在其操作日志(oplog,是一个 capped collection)中,从节点复制主节点的oplog日志并执行日志中的命令,以此保持数据和主节点一致。副本集的所有节点都可以进行读操作,但是应用程序默认从主节点读取数据。当主节点一段时间(当前默认为10s)不和从节点通信,集群就会开始投票选取新的主节点。下图来自官网,描述了一个一主两从的副本集的结构,应用程序的读写操作默认都是通过主节点进行的。

2 副本集搭建

  MongoDB的副本集搭建并不复杂,这里简单演示一下搭建过程。搭建mongoDB副本集时,节点的个数最好是奇数,这主要是为了保证投票顺利进行。这里演示搭建一个一主两从的副本集,搭建副本集时,每个节点最好部署在不同的设备上,因为我没有那么多电脑,所以就采用三台centos7虚拟机来搭建。

第一步 安装mongoDB  

  为了方便几台设备通信,我们在每台设备上使用 vim /etc/hosts 命令注册一下主机信息(注意要改成自己设备的ip),配置如下:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.70.129 mongo01
192.168.70.131 mongo02
192.168.70.133 mongo03

  在三台centos虚拟机上都安装mongoDB,安装可以参考第一篇中的安装方法,注意副本集的配置相比单机多了一个replication节点,这里设置副本集的名字为MongoXset,使用命令 vim /usr/local/mongodb/bin/mongodb.conf 编辑配置文件如下:

systemLog:
destination: file
logAppend: true
path: /usr/local/mongodb/logs/mongodb.log
storage:
dbPath: /usr/local/mongodb/data
journal:
enabled: true
processManagement:
fork: true
net:
port: 27017
bindIp: 0.0.0.0
replication:
replSetName: MongoXset

第二步 初始化副本集 

  安装完成后,在Robomongo中连接任意一个节点,执行以下命令初始化副本集:

//配置
config = { _id:"MongoXset", members:[
{_id:,host:"192.168.70.129:27017"},
{_id:,host:"192.168.70.131:27017"},
{_id:,host:"192.168.70.133:27017"}]
}
use admin
//初始化
rs.initiate(config)

  执行上边的命令后,我们的副本集就搭建完成了,执行 rs.status() 查看副本集的状态,我们看到192.168.70.133:27017的mongodb是primary(主节点),其他两个节点为secondary(从节点):

{
"set" : "MongoXset",
"date" : ISODate("2019-06-30T08:13:34.677Z"),
"myState" : ,
"term" : NumberLong(),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -,
"heartbeatIntervalMillis" : NumberLong(),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"appliedOpTime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"durableOpTime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
}
},
"lastStableCheckpointTimestamp" : Timestamp(, ),
"members" : [
{
"_id" : ,
"name" : "192.168.70.129:27017",
"health" : ,
"state" : ,
"stateStr" : "SECONDARY",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDurable" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDate" : ISODate("2019-06-30T08:13:27Z"),
"optimeDurableDate" : ISODate("2019-06-30T08:13:27Z"),
"lastHeartbeat" : ISODate("2019-06-30T08:13:33.585Z"),
"lastHeartbeatRecv" : ISODate("2019-06-30T08:13:34.465Z"),
"pingMs" : NumberLong(),
"lastHeartbeatMessage" : "",
"syncingTo" : "192.168.70.133:27017",
"syncSourceHost" : "192.168.70.133:27017",
"syncSourceId" : ,
"infoMessage" : "",
"configVersion" :
},
{
"_id" : ,
"name" : "192.168.70.131:27017",
"health" : ,
"state" : ,
"stateStr" : "SECONDARY",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDurable" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDate" : ISODate("2019-06-30T08:13:27Z"),
"optimeDurableDate" : ISODate("2019-06-30T08:13:27Z"),
"lastHeartbeat" : ISODate("2019-06-30T08:13:33.604Z"),
"lastHeartbeatRecv" : ISODate("2019-06-30T08:13:34.458Z"),
"pingMs" : NumberLong(),
"lastHeartbeatMessage" : "",
"syncingTo" : "192.168.70.133:27017",
"syncSourceHost" : "192.168.70.133:27017",
"syncSourceId" : ,
"infoMessage" : "",
"configVersion" :
},
{
"_id" : ,
"name" : "192.168.70.133:27017",
"health" : ,
"state" : ,
"stateStr" : "PRIMARY",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDate" : ISODate("2019-06-30T08:13:27Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -,
"infoMessage" : "",
"electionTime" : Timestamp(, ),
"electionDate" : ISODate("2019-06-30T08:10:05Z"),
"configVersion" : ,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
"ok" : ,
"operationTime" : Timestamp(, ),
"$clusterTime" : {
"clusterTime" : Timestamp(, ),
"signature" : {
"hash" : BinData(,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong()
}
}
}

3 测试副本集

  我们知道副本集的主节点负责所有写操作,从节点不能执行写操作,只会同步主节点的数据。这里简单测试一下:连接主节点192.168.70.133:27017,执行以下命令插入一条命令:

  连接从节点192.168.70.129:27017,上执行下边的命令,我们看到从节点是不能插入数据的,但是我们可以从从节点查到主节点插入的数据(注意:必须先执行 rs.slaveOk() 后才能进行read操作):

  测试高可用性:连接主节点192.168.70.133:27017,执行命令 use admin db.shutdownServer() 关闭主节点,然后连接一个其他节点执行 rs.status() 查看副本集状态如下,我们看到192.168.70.133:27017节点显示不可用,而192.168.70.129:27017被选举为新的主节点:

"members" : [
{
"_id" : ,
"name" : "192.168.70.129:27017",
"health" : ,
"state" : ,
"stateStr" : "PRIMARY",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDurable" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDate" : ISODate("2019-06-30T08:58:30Z"),
"optimeDurableDate" : ISODate("2019-06-30T08:58:30Z"),
"lastHeartbeat" : ISODate("2019-06-30T08:58:35.900Z"),
"lastHeartbeatRecv" : ISODate("2019-06-30T08:58:34.979Z"),
"pingMs" : NumberLong(),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -,
"infoMessage" : "",
"electionTime" : Timestamp(, ),
"electionDate" : ISODate("2019-06-30T08:50:58Z"),
"configVersion" :
},
{
"_id" : ,
"name" : "192.168.70.131:27017",
"health" : ,
"state" : ,
"stateStr" : "SECONDARY",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"optimeDate" : ISODate("2019-06-30T08:58:30Z"),
"syncingTo" : "192.168.70.129:27017",
"syncSourceHost" : "192.168.70.129:27017",
"syncSourceId" : ,
"infoMessage" : "",
"configVersion" : ,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : ,
"name" : "192.168.70.133:27017",
"health" : ,
"state" : ,
"stateStr" : "(not reachable/healthy)",
"uptime" : ,
"optime" : {
"ts" : Timestamp(, ),
"t" : NumberLong(-)
},
"optimeDurable" : {
"ts" : Timestamp(, ),
"t" : NumberLong(-)
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2019-06-30T08:58:36.291Z"),
"lastHeartbeatRecv" : ISODate("2019-06-30T08:50:59.539Z"),
"pingMs" : NumberLong(),
"lastHeartbeatMessage" : "Error connecting to 192.168.70.133:27017 :: caused by :: Connection refused",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -,
"infoMessage" : "",
"configVersion" : -
}
]

4 设置节点的优先级

  在部署的时候,我们一般更愿意让稳定且性能好的设备在选举时优先作为主节点,让性能差的服务器不能被选举为主节点。这就要用到优先级了,各个节点的默认优先级都是1,我们可以更改各个节点的优先级,优先级越高,被选举为主节点的几率就越大,优先级为0的节点不能被选举为主节点。使用mongo shell执行下边的命令就可以更改各个节点的优先级,这里就不再具体演示了。

//获取集群配置
  cfg=rs.config()
//设置优先级
  cfg.members[].priority=
  cfg.members[].priority=
  cfg.members[].priority=
//重新加载配置
  rs.reconfig(cfg)

3 副本集管理的常用函数

  这里汇总了一些管理副本集的相关命令,有兴趣的小伙伴可以自己测试一下:

方法 描述
 rs.status() 查看副本集状态 

rs.initiate(cfg)

初始化副本集 
 rs.conf()  获取副本集的配置
 rs.reconfig(cfg) 重新加载配置 
 rs.add(ip:port) 添加一个节点 
 rs.addArb(ip:port) 添加一个仲裁节点
 rs.remove(ip:port) 删除一个节点 
 rs.isMaster() 查看是否是主节点 
 rs.slaveOk() 让从节点可以执行read操作
rs.printReplicationInfo() 查看oplog的大小和时间
rs.printSlaveReplicationInfo() 查看从节点的数据同步情况

4 C#驱动之读写分离实现

  前边我们已经搭建了一个一主两从的副本集,状态为:192.168.70.129:27017(主节点),192.168.70.131:27017(从节点),192.168.70.133:27017(从节点),现在我们简单演示一下怎么使用C#操作副本集,并实现读写分离。

  首先添加一些测试数据:连接主节点192.168.70.129:27017,执行以下命令插入一些测试数据,接着到两个从节点分别执行命令 rs.slaveOk() 让节点可以进行read操作:

use myDb
//清空students中的记录
db.students.drop()
//在students集合中添加测试数据
db.students.insertMany([
{"no":, "stuName":"jack", "age":, "classNo":},
{"no":, "stuName":"tom", "age":, "classNo":},
{"no":, "stuName":"hanmeimei", "age":, "classNo":},
{"no":, "stuName":"lilei", "age":, "classNo":}
])

  然后写一个控制台程序,使用 Install-Package MongoDB.Driver 添加驱动包,具体代码如下:

    class Program
{
static void Main(string[] args)
{
//连接数据库
var client = new MongoClient("mongodb://192.168.70.133:27017, 192.168.70.131:27017, 192.168.70.129:27017");
//获取database
var mydb = client.GetDatabase("myDb");
//设置优先从从节点读取数据
mydb.WithReadPreference(ReadPreference.Secondary);
//mydb.WithReadConcern(ReadConcern.Majority);
//mydb.WithWriteConcern(WriteConcern.WMajority);//这里可以设置写入确认级别
//获取collection
var stuCollection = mydb.GetCollection<Student>("students");
//插入一条数据
stuCollection.InsertOne(new Student()
{
no = ,
stuName = "jim",
age =
}); //读取学生列表
List<Student> stuList = stuCollection.AsQueryable().ToList();
stuList.ForEach(s => Console.WriteLine($"学号:{s.no} ,姓名:{s.stuName} ,年龄:{s.age}"));
Console.ReadKey();
}
}
/// <summary>
/// 学生类
/// </summary
public class Student
{
public int no { get; set; }//学号
public string stuName { get; set; }//姓名
public int age { get; set; }//年龄
[BsonExtraElements]
public BsonDocument others { get; set; }
}

  注意一点:使用  var client = new MongoClient("mongodb://192.168.70.133:27017, 192.168.70.131:27017, 192.168.70.129:27017"); 获取client时,驱动程序能够自动判断哪个节点是主节点。执行结果如下:

2 Sharing分片简介

  除了副本集,在Mongodb里面存在另一种集群:分片集群。所谓分片简单来说就是将一个完整的数据分割后存储在不同的节点上。当MongoDB存储海量的数据时,一台机器不足以存储数据,或者数据过多造成读写吞吐量不能满足我们的需求时,可以考虑使用分片集群。

  举一个栗子:例如我们有1个亿的用户信息,选择用户的name列作为分片键(shard key),将用户信息存储到两个Shard Server中,mongoDB会自动根据分片键将用户数据进行分片,假如分片后第一个片(shard1)存储了名字首字母为a~m的用户信息,第二个片(shard2)存储了名字首字母为n~z的用户信息。当我们要查询name=jack的用户时,因为jack的首字母j在a和m之间,所以分片集群会直接到shard1中查找,这样查询效率就会提高很多;如果我们要插入一个name=tom的用户,因为t在n~z之间,所以tom的信息会插入shard2中。这个栗子的分片键是name,当我们要查询生日为某一天的用户时(出生日期不是分片键),mongoDB还是会去查询所有分片服务器的数据。

  分片集群的基本结构如下:

  分片集群主要包含三个组件(都是mongod进程):

1 Shard Server

    存储角色,真实的业务数据都保存在该角色中。在生产环境中每一个shard server都应该使用副本集,用于防止数据丢失,实现高可用。

2 Config Server

  配置角色,存储sharing集群的元数据和配置信息。分片集群判断我们查询的数据在哪个shard中,或者要将数据插入到哪一个shard中就是由Config Server中的配置决定的。Config Server也要使用副本集充当,不然Config Server宕机,配置信息就无从获取了。

3 mongos

  路由角色,这是应用程序访问分片集群的入口,我们通过连接mongos来访问分片集群,mongos让整个集群看起来就像一个单独的数据库。mongos同样推荐配置成副本集,不然路由角色宕机,应用程序就无法访问集群。

  分片集群各个角色一般都要配置为副本集,所以需要较多的mongod进程,如sharing 集群中的三个角色都使用一主两从的副本集就需要9个mongod进程,这里就不再具体演示怎么去搭建分片集群,有兴趣的小伙伴可以按照官网文档搭建,或者参考园友努力哥的文章

  对于中小型应用,使用副本集就可以满足业务需求,没必要使用分片集群。当数据量非常大时我们可以考虑使用分片技术。在开发中使用分片集群时,只需要把mongos作为一个简单的mongoDB实例连接即可,至于怎么去分片存储和分片查询会由集群自动完成。关于mongoDB的副本集和分片技术就简单介绍到这里,本节也是mongoDB的最后一篇,更深入的应用以后在业务需要时继续研究。如果文中有错误的话,希望大家可以指出,我会及时修改,谢谢!

快速掌握mongoDB(五)——读写分离的副本集实现和Sharing介绍的更多相关文章

  1. mongodb的读写分离

    转自:http://blog.csdn.net/sd0902/article/details/21538621 mongodb的读写分离使用Replica Sets来实现 对于replica set ...

  2. MongoDB学习笔记——Replica Set副本集

    副本集 可以将MongoDB中的副本集看作一组服务器集群由一个主节点和多个副本节点等组成,相对于之前讲到的主从复制提供了故障自动转移的功能 副本集实现数据同步的方式依赖于local数据库中的oplog ...

  3. Mycat搭建负载均衡,读写分离的Mysql集群

    Mycat搭建负载均衡,读写分离的Mysql集群 准备环境 1.mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz 2.Mycat-server-1.6.7.4-te ...

  4. Dubbo入门到精通学习笔记(二十):MyCat在MySQL主从复制的基础上实现读写分离、MyCat 集群部署(HAProxy + MyCat)、MyCat 高可用负载均衡集群Keepalived

    文章目录 MyCat在MySQL主从复制的基础上实现读写分离 一.环境 二.依赖课程 三.MyCat 介绍 ( MyCat 官网:http://mycat.org.cn/ ) 四.MyCat 的安装 ...

  5. MongoDB数据库 : 管道,用户管理,副本集等

    聚合(aggregate): db.集合.aggregate([{管道:{表达式}}]) db.集合.aggregate([ {管道1:{表达式1}}, {管道2:{表达式2}}, ... ...]) ...

  6. windows NLB实现MSSQL读写分离--从数据库集群读负载均衡

    主从模式,几乎大部分出名的数据库都支持的一种集群模式. 当Web站点的访问量上去之后,很多站点,选择读写分离,减轻主数据库的的压力.当然,一主多从也可以作用多个功能,比如备份.这里主要演示如何实现从数 ...

  7. 修改mongodb(带仲裁节点的副本集)各机器端口

    需求:因为端口调整,需要改变副本的备份集 1.查看当前的副本集信息 [root@localhost bin]# ./mongo 192.168.1.134:10001 repltest:PRIMARY ...

  8. MongoDB 3.4 分片 由副本集组成

    要在真实环境中实现MongoDB分片至少需要四台服务器做分片集群服务器,其中包含两个Shard分片副本集(每个包含两个副本节点及一个仲裁节点).一个配置副本集(三个副本节点,配置不需要仲裁节点),其中 ...

  9. MySQL读写分离高可用集群及读操作负载均衡(Centos7)

    目录 概述 keepalived和heartbeat对比 一.环境 二.部署 部署lvs代理和keepalived MySQL+heartbeat+drbd的部署 MySQL主从复制 web服务器及a ...

随机推荐

  1. Delphi产生任务栏图标【TNotifyIconData】

    一.新建一个应用程序:File->New Applicaton 在Interface部分要放在Uses Message之后,定义一个消息常量:const WM_NID=WM_USER+1000; ...

  2. React躬行记(5)——React和DOM

    React实现了一套与浏览器无关的DOM系统,包括元素渲染.节点查询.事件处理等机制. 一.ReactDOM 自React v0.14开始,官方将与DOM相关的操作从React中剥离,组成单独的rea ...

  3. HBase 学习之路(八)——HBase协处理器

    一.简述 在使用HBase时,如果你的数据量达到了数十亿行或数百万列,此时能否在查询中返回大量数据将受制于网络的带宽,即便网络状况允许,但是客户端的计算处理也未必能够满足要求.在这种情况下,协处理器( ...

  4. spring boot 2.x 系列 —— spring boot 整合 redis

    文章目录 一.说明 1.1 项目结构 1.2 项目主要依赖 二.整合 Redis 2.1 在application.yml 中配置redis数据源 2.2 封装redis基本操作 2.3 redisT ...

  5. spring cloud 系列第8篇 —— config+bus 分布式配置中心与配置热刷新 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.config 简介 spring cloud config 分为服务端 ...

  6. 老雷socket编程之PHP利用socket扩展实现聊天服务

    老雷socket编程之PHP利用socket扩展实现聊天服务 socket聊天服务原理 PHP有两个socket的扩展 sockets和streamssockets socket_create(AF_ ...

  7. Akka-CQRS(14)- Http标准安全解决方案:OAuth2-资源使用授权

    上一篇讨论了SSL/TLS安全连接,主要是一套在通信层面的数据加密解决方案.但我们更需要一套方案来验证客户端.要把不能通过验证的网络请求过滤掉. OAuth2是一套行业标准的网络资源使用授权协议,也就 ...

  8. 简单的python爬虫实例

    目标网站:https://www.quanjing.com/category/1286521/2.html 爬取搜索出来的所有“中东人”的图片: 先看一下源代码,找到存放图片链接的地方,在源代码最下面 ...

  9. java 中的一些运算符问题

    逻辑运算符 && 与 &: 在这二个与运算符中,一般用于if的判断中,A&&B,假设A的条件不满足时 则不会去判断后面的B, 如果A满足条件时就会接下来去做B条 ...

  10. php中对象类型与数组之间的转换

    1.刚看视频学习的时候看到一个困扰很久的问题, 有时候我们在进行做项目的时候会碰到的一个小问题.举一个小例子.  获取一个xml文件里面的数据. xml.xml文件如下: <?xml versi ...