快速掌握mongoDB(五)——读写分离的副本集实现和Sharing介绍
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介绍的更多相关文章
- mongodb的读写分离
转自:http://blog.csdn.net/sd0902/article/details/21538621 mongodb的读写分离使用Replica Sets来实现 对于replica set ...
- MongoDB学习笔记——Replica Set副本集
副本集 可以将MongoDB中的副本集看作一组服务器集群由一个主节点和多个副本节点等组成,相对于之前讲到的主从复制提供了故障自动转移的功能 副本集实现数据同步的方式依赖于local数据库中的oplog ...
- Mycat搭建负载均衡,读写分离的Mysql集群
Mycat搭建负载均衡,读写分离的Mysql集群 准备环境 1.mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz 2.Mycat-server-1.6.7.4-te ...
- Dubbo入门到精通学习笔记(二十):MyCat在MySQL主从复制的基础上实现读写分离、MyCat 集群部署(HAProxy + MyCat)、MyCat 高可用负载均衡集群Keepalived
文章目录 MyCat在MySQL主从复制的基础上实现读写分离 一.环境 二.依赖课程 三.MyCat 介绍 ( MyCat 官网:http://mycat.org.cn/ ) 四.MyCat 的安装 ...
- MongoDB数据库 : 管道,用户管理,副本集等
聚合(aggregate): db.集合.aggregate([{管道:{表达式}}]) db.集合.aggregate([ {管道1:{表达式1}}, {管道2:{表达式2}}, ... ...]) ...
- windows NLB实现MSSQL读写分离--从数据库集群读负载均衡
主从模式,几乎大部分出名的数据库都支持的一种集群模式. 当Web站点的访问量上去之后,很多站点,选择读写分离,减轻主数据库的的压力.当然,一主多从也可以作用多个功能,比如备份.这里主要演示如何实现从数 ...
- 修改mongodb(带仲裁节点的副本集)各机器端口
需求:因为端口调整,需要改变副本的备份集 1.查看当前的副本集信息 [root@localhost bin]# ./mongo 192.168.1.134:10001 repltest:PRIMARY ...
- MongoDB 3.4 分片 由副本集组成
要在真实环境中实现MongoDB分片至少需要四台服务器做分片集群服务器,其中包含两个Shard分片副本集(每个包含两个副本节点及一个仲裁节点).一个配置副本集(三个副本节点,配置不需要仲裁节点),其中 ...
- MySQL读写分离高可用集群及读操作负载均衡(Centos7)
目录 概述 keepalived和heartbeat对比 一.环境 二.部署 部署lvs代理和keepalived MySQL+heartbeat+drbd的部署 MySQL主从复制 web服务器及a ...
随机推荐
- Compile for Windows on Linux(交叉编译,在Linux下编译Windows程序),以OpenSSL为例
OpenSSL for Windows In earlier articles, we have looked at how to create a gcc build environment on ...
- Indy9的TIdFTPServer封装类
在Delphi 7开发下有强大的Indy控件,版本为9,要实现一个FTP服务器,参考自带的例子,发现还要写很多函数,而且不支持中文显示文件列表等等.于是,自己改进封装了下,形成一个TFTPServer ...
- 取得文件夹内容信息(使用IShellFolder接口)
翻译自MSDN 2005 -> Win32 和 COM 开发 -> User Interface -> Windows User Experience -> Windows S ...
- flask(一)
一.python现阶段三大主流框架Django Tornado Flask的对比 特点: 1.Django的特点是大而全,集成了很多组件,属于全能型框架 2.tornado的主要特点是原生异步非阻塞, ...
- l论文查重平台
推荐大家一个靠谱的论文检测平台.重复的部分有详细出处以及具体修改意见,能直接在文章上做修改,全部改完一键下载就搞定了.怕麻烦的话,还能用它自带的降重功能.哦对了,他们现在正在做毕业季活动, 赠送很多免 ...
- Django 强大的ORM之增删改查
Django orm Django——orm进阶 测试Django中的orm 表结构: models.py class User(models.Model): name = model ...
- 微服务之服务注册与发现--Consul(转载)
http://blog.csdn.net/buxiaoxia/article/details/69788114 https://www.cnblogs.com/xiaohanlin/p/8016803 ...
- 17 | 精益求精:聊聊提高GUI测试稳定性的关键技术
- centos安装netcat TCP UDP测试工具 简称 nc,安全界叫它瑞士军刀
centos安装netcat 今天安装swoole后,测试UDP服务需要用到netcat,然而百度了很多安装方法,并没有一个好用的.几经尝试,终于安装成功,现在就分享给大家,以供参考. 配置环境:ce ...
- 记一次基于Cloudflare服务的爬虫
前言 前几天有个朋友托我帮忙爬一个网站,第一次打开那个网站时,我去,它竟然就要验证码,不过当时是想加几个header应该就能解决,不过事实证明我还是错了.接下来将记录下爬虫中遇到的一些坑以及解决办法. ...