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. 简单有趣的hover

    一张图片在鼠标悬浮的时候换另一张图片 平常写张图片用img我们换一种方法用背景,然后就悬浮出 我们在加个边框试试 怎么才能让边框一起换哪? 我们嵌套一下 然后打开就是 是不是一个很简单的hover

  2. idea创建类报错

    创建类报错: 在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置 -Djdk.util.zip.ensureTrailingSlash=false jar包 ...

  3. Java学习笔记——XML入门

    以下内容来自网络 什么是 XML? XML 指可扩展标记语言(EXtensible Markup Language). XML 是一种很像HTML的标记语言. XML 的设计宗旨是传输数据,而不是显示 ...

  4. 详解Linux运维工程师必备技能

    张戈大神是腾讯的一名运维,张戈博客也是我接触到第一个 Linux 运维师的博客,最近也在接触 Linux,说到工具,在行外可以说是技能,在行内一般称为工具,就是运维必须要掌握的工具. 我就大概列出这几 ...

  5. Python random() 生成随机数

    random() 函数中常见的函数如下: #!/usr/bin/python # -*- coding: UTF-8 -*- import random print( random.randint(1 ...

  6. Oracle Awr报告_生成

    AWR的概念 Oracle数据库是一个使用量很多的数据库,关于Oracle数据库的性能.Oracle10g以后,Oracle提供了一个性能检测的工具:AWR(Automatic Workload Re ...

  7. SQLServer常用运维SQL整理

    今天线上SQLServer数据库的CPU被打爆了,紧急情况下,分析了数据库阻塞.连接分布.最耗CPU的TOP10 SQL.查询SQL并行度配置.查询SQL 重编译的原因等等 整理了一些常用的SQL 1 ...

  8. DFS(二):骑士游历问题

    在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次.例如,下图给出了骑士从坐标(1,5)出发,游历棋盘的一种可能情况. [例1] ...

  9. Quartz每次调度时被执行两次

    [关键字:重复执行.重复调用.每次执行两次.执行2次] 前言: 先说一下,项目背景.由于组内某成员在用Maven搭建项目时不规范,导致项目的名称与实际访问项目名称不一致.在部署项目时,必需要配一下虚拟 ...

  10. POJ 2778:DNA Sequence(AC自动机构造矩阵)

    http://poj.org/problem?id=2778 题意:有m个病毒DNA,问构造一个长度为n的不带病毒DNA的字符串可以有多少种. 思路:看到这题有点懵,想了挺久题解的思路. 使用AC自动 ...