Web架构之路:MongoDB集群及高可用实践
MongoDB集群有副本集及主从复制两种模式,不过主从模式在MongoDB 3.6已经彻底废弃,今天主要探讨副本集的搭建和使用,以及分片。
副本集介绍
副本集(Replica Set)即副本的集合,在MongoDB中通过先定义一个副本集合,然后将多个节点(副本)加入到这个集合中。简单来说就是集群中包含了多份数据,保证主节点挂掉,备节点能够继续提供数据服务,实现MongoDB的数据备份及高可用。
副本集具有以下特征:
- N 个节点的集群
- 任何节点可作为主节点
- 所有写入操作都在主节点上
- 自动故障转移
- 自动恢复
副本集搭建
条件有限,我们在单机上,通过三个不同的MongoD线程来搭副本集。
主节点配置如下:
- # 指定数据库路径
- dbpath=/usr/local/mongochina/data/db
- # 使用追加的方式写日志
- logpath=/usr/local/mongochina/log/mongodb.log
- # 使用追加的方式写日志
- logappend = true
- # 绑定服务IP
- bind_ip=127.0.0.1
- # 服务器端口
- port = 27017
- # 以守护进程的方式运行MongoDB,创建服务器进程
- fork = true
- # PID File 的完整路径
- pidfilepath=/usr/local/mongochina/var/mongod.pid
- # 不启用验证
- noauth=true
- # 最大同时连接数,默认2000
- maxConns=2000
- # 同步复制的日志大小设置,单位MB
- oplogSize=10
- # 副本集名称
- replSet=rs0
副本节点的配置和主节点的基本一致,需要修改一下数据库/日志/PID路径和端口号,副本集名称需一致:
- # 指定数据库路径
- dbpath=/usr/local/mongochina/node/2/data/db
- # 使用追加的方式写日志
- logpath=/usr/local/mongochina/node/2/log/mongodb.log
- # 使用追加的方式写日志
- logappend = true
- # 绑定服务IP
- bind_ip=127.0.0.1
- # 服务器端口
- port = 27018
- # 以守护进程的方式运行MongoDB,创建服务器进程
- fork = true
- # PID File 的完整路径
- pidfilepath=/usr/local/mongochina/var/mongod2.pid
- # 不启用验证
- noauth=true
- # 最大同时连接数,默认2000
- maxConns=2000
- # 副本集
- replSet=rs0
依次启动三个mongod进程:
- gitlib@devops:/usr/local/mongodb$ ps -aux | grep mongod
- root 14293 0.8 2.3 1588812 92700 ? Sl 08:06 0:01 bin/mongod -f mongod.conf
- root 14652 3.5 2.2 1583180 89364 ? Sl 08:08 0:00 bin/mongod -f mongod2.conf
- root 14723 6.4 2.2 1583180 89172 ? Sl 08:08 0:00 bin/mongod -f mongod3.conf
在主节点中,先使用rs.initiate()方法进行副本集初始化操作,再使用rs.add()方法来添加副本集的成员:
- > rs.initiate()
- {
- “info2” : “no configuration specified. Using a default configuration for the set”,
- “me” : “127.0.0.1:27017”,
- “ok” : 1,
- “$clusterTime” : {
- “clusterTime” : Timestamp(1569457173, 1),
- “signature” : {
- “hash” : BinData(0,“AAAAAAAAAAAAAAAAAAAAAAAAAAA=”),
- “keyId” : NumberLong(0)
- }
- },
- “operationTime” : Timestamp(1569457173, 1)
- }
- rs0:OTHER> rs.add(‘127.0.0.1:27018’);
- {
- “ok” : 1,
- “$clusterTime” : {
- “clusterTime” : Timestamp(1569457214, 2),
- “signature” : {
- “hash” : BinData(0,“AAAAAAAAAAAAAAAAAAAAAAAAAAA=”),
- “keyId” : NumberLong(0)
- }
- },
- “operationTime” : Timestamp(1569457214, 2)
- }
- rs0:PRIMARY> rs.add(‘127.0.0.1:27019’);
- {
- “ok” : 1,
- “$clusterTime” : {
- “clusterTime” : Timestamp(1569457219, 1),
- “signature” : {
- “hash” : BinData(0,“AAAAAAAAAAAAAAAAAAAAAAAAAAA=”),
- “keyId” : NumberLong(0)
- }
- },
- “operationTime” : Timestamp(1569457219, 1)
- }
- rs0:PRIMARY>
到此,MongoDB副本集部署完成,我们可以通过rs.status()命令查看副本集状态。
- gitlib@devops:~$ mongo 127.0.0.1:27018
- rs0:SECONDARY> rs.status()
- {
- “set” : “rs0”,
- “date” : ISODate(“2019-09-26T12:09:48.818Z”),
- “myState” : 2,
- “term” : NumberLong(1),
- “syncingTo” : “127.0.0.1:27017”,
- “syncSourceHost” : “127.0.0.1:27017”,
- “syncSourceId” : 0,
- “heartbeatIntervalMillis” : NumberLong(2000),
- “optimes” : {
- “lastCommittedOpTime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “lastCommittedWallTime” : ISODate(“2019-09-26T12:09:46.038Z”),
- “readConcernMajorityOpTime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “readConcernMajorityWallTime” : ISODate(“2019-09-26T12:09:46.038Z”),
- “appliedOpTime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “durableOpTime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “lastAppliedWallTime” : ISODate(“2019-09-26T12:09:46.038Z”),
- “lastDurableWallTime” : ISODate(“2019-09-26T12:09:46.038Z”)
- },
- “lastStableRecoveryTimestamp” : Timestamp(1569499726, 1),
- “lastStableCheckpointTimestamp” : Timestamp(1569499726, 1),
- “members” : [
- {
- “_id” : 0,
- “name” : “127.0.0.1:27017”,
- “ip” : “127.0.0.1”,
- “health” : 1,
- “state” : 1,
- “stateStr” : “PRIMARY”,
- “uptime” : 42574,
- “optime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “optimeDurable” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “optimeDate” : ISODate(“2019-09-26T12:09:46Z”),
- “optimeDurableDate” : ISODate(“2019-09-26T12:09:46Z”),
- “lastHeartbeat” : ISODate(“2019-09-26T12:09:47.119Z”),
- “lastHeartbeatRecv” : ISODate(“2019-09-26T12:09:47.667Z”),
- “pingMs” : NumberLong(0),
- “lastHeartbeatMessage” : “”,
- “syncingTo” : “”,
- “syncSourceHost” : “”,
- “syncSourceId” : -1,
- “infoMessage” : “”,
- “electionTime” : Timestamp(1569457173, 2),
- “electionDate” : ISODate(“2019-09-26T00:19:33Z”),
- “configVersion” : 3
- },
- {
- “_id” : 1,
- “name” : “127.0.0.1:27018”,
- “ip” : “127.0.0.1”,
- “health” : 1,
- “state” : 2,
- “stateStr” : “SECONDARY”,
- “uptime” : 43284,
- “optime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “optimeDate” : ISODate(“2019-09-26T12:09:46Z”),
- “syncingTo” : “127.0.0.1:27017”,
- “syncSourceHost” : “127.0.0.1:27017”,
- “syncSourceId” : 0,
- “infoMessage” : “”,
- “configVersion” : 3,
- “self” : true,
- “lastHeartbeatMessage” : “”
- },
- {
- “_id” : 2,
- “name” : “127.0.0.1:27019”,
- “ip” : “127.0.0.1”,
- “health” : 1,
- “state” : 2,
- “stateStr” : “SECONDARY”,
- “uptime” : 42569,
- “optime” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “optimeDurable” : {
- “ts” : Timestamp(1569499786, 1),
- “t” : NumberLong(1)
- },
- “optimeDate” : ISODate(“2019-09-26T12:09:46Z”),
- “optimeDurableDate” : ISODate(“2019-09-26T12:09:46Z”),
- “lastHeartbeat” : ISODate(“2019-09-26T12:09:47.646Z”),
- “lastHeartbeatRecv” : ISODate(“2019-09-26T12:09:47.036Z”),
- “pingMs” : NumberLong(0),
- “lastHeartbeatMessage” : “”,
- “syncingTo” : “127.0.0.1:27018”,
- “syncSourceHost” : “127.0.0.1:27018”,
- “syncSourceId” : 1,
- “infoMessage” : “”,
- “configVersion” : 3
- }
- ],
- “ok” : 1,
- “$clusterTime” : {
- “clusterTime” : Timestamp(1569499786, 1),
- “signature” : {
- “hash” : BinData(0,“AAAAAAAAAAAAAAAAAAAAAAAAAAA=”),
- “keyId” : NumberLong(0)
- }
- },
- “operationTime” : Timestamp(1569499786, 1)
- }
副本集高可用
集群中的各节点还会通过传递心跳信息来检测各自的健康状况。当主节点故障时,多个从节点会触发一次 新的选举操作,并选举其中的一个成为新的主节点(通常谁的优先级更高,谁就是新的主节点),心跳信息默认每 2 秒传递一次。
客户端连接到副本集后,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份。一旦主节点挂掉,副本节点就会选举一个新的主服务器。这一切对于应用服务器不需要关心。
我们可以通过关闭主节点,测试是否会选举新的主节点:
- gitlib@devops:~$ ps -aux | grep mongod
- root 14293 0.6 2.5 1888584 99504 ? Sl 08:06 4:39 bin/mongod -f mongod.conf
- root 14652 0.6 2.6 1923896 102200 ? Sl 08:08 4:59 bin/mongod -f mongod2.conf
- root 14723 0.6 2.5 1886124 98984 ? Sl 08:08 4:47 bin/mongod -f mongod3.conf
- gitlib@devops:~$ sudo kill -9 14293
- [sudo] password for zhoufei:
- zhoufei@devops:~$ ps -aux | grep mongod
- root 14652 0.6 2.6 1932092 102200 ? Sl 08:08 4:59 bin/mongod -f mongod2.conf
- root 14723 0.6 2.5 1894320 99064 ? Sl 08:08 4:47 bin/mongod -f mongod3.conf
我们直接kill掉主节点,进入节点1,看一下当前节点是否是主节点:
- gitlib@devops:~$ mongo 127.0.0.1:27018
- rs0:SECONDARY> rs.isMaster()
- {
- “hosts” : [
- “127.0.0.1:27017”,
- “127.0.0.1:27018”,
- “127.0.0.1:27019”
- ],
- “setName” : “rs0”,
- “setVersion” : 3,
- “ismaster” : false,
- “secondary” : true,
- “primary” : “127.0.0.1:27019”,
- “me” : “127.0.0.1:27018”,
- …
可以看到当主节点(127.0.0.1:27017)挂掉之后,主节点自动切换到从节点2(127.0.0.1:27019)上。
副本集选举机制
副本集中的从节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举出一位新的主服务器。
副本集包括三种节点:主节点、从节点、仲裁节点。
- 主节点负责处理客户端请求,读、写数据, 记录在其上所有操作的oplog;
- 从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。默认情况下,从节点不支持外部读取,但可以设置,副本集的机制在于主节点出现故障的时候,余下的节点会选举出一个新的主节点,从而保证系统可以正常运行。
- 仲裁节点不复制数据,仅参与投票。由于它没有访问的压力,比较空闲,因此不容易出故障。由于副本集出现故障的时候,存活的节点必须大于副本集节点总数的一半,否则无法选举主节点,或者主节点会自动降级为从节点,整个副本集变为只读。因此,增加一个不容易出故障的仲裁节点,可以增加有效选票,降低整个副本集不可用的风险。仲裁节点可多于一个。也就是说只参与投票,不接收复制的数据,也不能成为活跃节点。
官方推荐MongoDB副本节点最少为3台, 建议副本集成员为奇数,最多12个副本节点,最多7个节点参与选举。限制副本节点的数量,主要是因为一个集群中过多的副本节点,增加了复制的成本,反而拖累了集群的整体性能。 太多的副本节点参与选举,也会增加选举的时间。而官方建议奇数的节点,是为了避免脑裂 的发生。
选举过程
副本集的选举过程大致如下:
得到每个服务器节点的最后操作时间戳。每个 mongodb都有oplog机制会记录本机的操作,方便和主服务器进行对比数据是否同步还可以用于错误恢复。
如果集群中大部分服务器down机了,保留活着的节点都为secondary状态并停止,不选举了。
如果集群中选举出来的主节点或者所有从节点最后一次同步时间看起来很旧了,停止选举等待人来操作。
如果上面都没有问题就选择最后操作时间戳最新(保证数据是最新的)的服务器节点作为主节点。
MongoDB 同步延迟问题
在MongoDB中,所有写操作都会产生 oplog,oplog 是每修改一条数据都会生成一条,如果你采用一个批量update命令更新了 N 多条数据,那么oplog 会有很多条,而不是一条。所以同步延迟就是写操作在主节点上执行完后,从节点还没有把 oplog 拿过来再执行一次。而这个写操作的量越大,主节点与从节点的差别也就越大,同步延迟也就越大了。
分片
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
分片集群结构分布:
三个主要组件:
- Shard:数据存储位置,以chunk为单位存数据,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障;
- Config Server:mongod实例,存储了整个ClusterMetadata,其中包括 chunk信息,默认需要配置3个Config Server节点;
- Query Routers:(Mongos) 前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
Mongos本身并不持久化数据,Sharded Cluster所有的元数据都会存储到Config Server,而用户的数据会议分散存储到各个shard。Mongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的碎片。
Mongos的路由功能:
- 当数据写入时,MongoDB Cluster根据分片键设计写入数据。
- 当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。
分片部署
条件有限,我们还是在单机上,用不同MongoDB线程来部署分片。
分片服务器
Shard Server和普通Mongod程序一样,不同的是需要在配置文件中添加shardsvr=true标记为Shard Server,配置参考如下:
- # 指定数据库路径
- dbpath=/usr/local/mongochina/share/1/data/db
- # 使用追加的方式写日志
- logpath=/usr/local/mongochina/share/1/log/mongodb.log
- # 使用追加的方式写日志
- logappend = true
- # 绑定服务IP
- bind_ip=127.0.0.1
- # 服务器端口
- port = 27020
- # 以守护进程的方式运行MongoDB,创建服务器进程
- fork = true
- # PID File 的完整路径
- pidfilepath=/usr/local/mongochina/var/mongod27020.pid
- # 不启用验证
- noauth=true
- # 最大同时连接数,默认2000
- maxConns=2000
- # 同步复制的日志大小设置,单位MB
- oplogSize=10
- # 设置为shared server
- shardsvr=true
以上配置复制4份,修改一下数据库路径/日志路径/服务器IP和端口/PID路径,启动4个Shard Server:
- sudo bin/mongod -f shard1.conf
- sudo bin/mongod -f shard2.conf
- sudo bin/mongod -f shard3.conf
- sudo bin/mongod -f shard4.conf
配置服务器
4.0版本的MongoDB中配置服务器(Config Server)需要设置副本集,同时设置configsvr=true,配置参考如下:
- # 指定数据库路径
- dbpath=/usr/local/mongochina/share/5/data/db
- # 使用追加的方式写日志
- logpath=/usr/local/mongochina/share/5/log/mongodb.log
- # 使用追加的方式写日志
- logappend = true
- # 绑定服务IP
- bind_ip=127.0.0.1
- # 服务器端口
- port = 27100
- # 以守护进程的方式运行MongoDB,创建服务器进程
- fork = true
- # PID File 的完整路径
- pidfilepath=/usr/local/mongochina/var/mongod27100.pid
- # 不启用验证
- noauth=true
- # 最大同时连接数,默认2000
- maxConns=2000
- # 同步复制的日志大小设置,单位MB
- oplogSize=10
- # 配置为config server
- configsvr=true
- # 副本集名称
- replSet=rs0
启动Config Server,并初始化副本集:
- sudo bin/mongod -f shard-config.conf
- mongo 127.0.0.1:27100
- > rs.initiaze()
新版本MongoDB建议设置多个Config Server,采用副本集形式设置集群,为了搭建方便,这里我们只采用单个Config Server。
路由服务器
Router Server不存放数据,配置参考如下:
- # 使用追加的方式写日志
- logpath=/usr/local/mongochina/share/6/log/mongodb.log
- # 使用追加的方式写日志
- logappend = true
- # 绑定服务IP
- bind_ip=127.0.0.1
- # 服务器端口
- port = 4000
- # 以守护进程的方式运行MongoDB,创建服务器进程
- fork = true
- # PID File 的完整路径
- pidfilepath=/usr/local/mongochina/var/mongod4000.pid
- # 设置监听的config服务器
- configdb=rs0/127.0.0.1:27100
启动Router Server,路由服务器是由mongos命令启动,与分片服务器及配置服务器不同。
- sudo bin/mongos -f shard-router.conf
启动后,需要通过sh.addShard()命令添加分片服务器:
- sh.addShard(‘127.0.0.1:27020’)
- sh.addShard(‘127.0.0.1:27021’)
- sh.addShard(‘127.0.0.1:27022’)
- sh.addShard(‘127.0.0.1:27023’)
配置完成后,可以通过sh.status()命令,查看分片情况:
- mongos> sh.status()
- — Sharding Status —
- sharding version: {
- “_id” : 1,
- “minCompatibleVersion” : 5,
- “currentVersion” : 6,
- “clusterId” : ObjectId(“5d8ddd1d94796dc650e29f67”)
- }
- shards:
- { “_id” : “shard0000”, “host” : “127.0.0.1:27020”, “state” : 1 }
- { “_id” : “shard0001”, “host” : “127.0.0.1:27021”, “state” : 1 }
- { “_id” : “shard0002”, “host” : “127.0.0.1:27022”, “state” : 1 }
- { “_id” : “shard0003”, “host” : “127.0.0.1:27023”, “state” : 1 }
- active mongoses:
- “4.2.0” : 1
- autosplit:
- Currently enabled: yes
- balancer:
- Currently enabled: yes
- Currently running: no
- Failed balancer rounds in last 5 attempts: 0
- Migration Results for the last 24 hours:
- No recent migrations
- databases:
- { “_id” : “config”, “primary” : “config”, “partitioned” : true }
- config.system.sessions
- shard key: { “_id” : 1 }
- unique: false
- balancing: true
Web架构之路:MongoDB集群及高可用实践的更多相关文章
- K8S集群Master高可用实践
K8S集群Master高可用实践 https://blog.51cto.com/ylw6006/2164981 本文将在前文基础上介绍k8s集群的高可用实践,一般来讲,k8s集群高可用主要包含以 ...
- linux运维、架构之路-Kubernetes集群部署TLS双向认证
一.kubernetes的认证授权 Kubernetes集群的所有操作基本上都是通过kube-apiserver这个组件进行的,它提供HTTP RESTful形式的API供集群内外客户端调 ...
- 15套java架构师、集群、高可用、高可扩展、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战视频教程
* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩展. ...
- 15套java架构师、集群、高可用、高可扩 展、高性能、高并发、性能优化Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战视频教程
* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展 ...
- 浅谈web应用的负载均衡、集群、高可用(HA)解决方案(转)
1.熟悉几个组件 1.1.apache —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代理服务器,支持安 ...
- hadoop 集群HA高可用搭建以及问题解决方案
hadoop 集群HA高可用搭建 目录大纲 1. hadoop HA原理 2. hadoop HA特点 3. Zookeeper 配置 4. 安装Hadoop集群 5. Hadoop HA配置 搭建环 ...
- Rabbitmq安装、集群与高可用配置
历史: RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有很多 ...
- 11.Redis 哨兵集群实现高可用
作者:中华石杉 Redis 哨兵集群实现高可用 哨兵的介绍 sentinel,中文名是哨兵.哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能: 集群监控:负责监控 redis mast ...
- Dubbo入门到精通学习笔记(十五):Redis集群的安装(Redis3+CentOS)、Redis集群的高可用测试(含Jedis客户端的使用)、Redis集群的扩展测试
文章目录 Redis集群的安装(Redis3+CentOS) 参考文档 Redis 集群介绍.特性.规范等(可看提供的参考文档+视频解说) Redis 集群的安装(Redis3.0.3 + CentO ...
随机推荐
- 前端,DJ
Vue模块 1.Vue都有哪些指令,简单说说? """ Vue里面常见指令有文本指令:v-text.v-html,属性指令:v-bind,方法指令:v-on,条件指令:v ...
- Makefile之编译运行连接库方法
LIBS+= -L $$PWD/../HKUnifyCamera_one/Debug -lHKUnifyCamera -luuid -Wl,-rpath=$$PWD/../HKUnifyCamera_ ...
- 绘制matplotlib 饼状图
参考:https://blog.csdn.net/ScarlettYellow/article/details/80458797 (2)2016年就业人员在三次产业中分布的饼状图. def swap( ...
- Spring Cloud @RefreshScope 原理是什么?
要清楚RefreshScope,先要了解Scope Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0开始就有的核心的概念 ...
- salt修改主机名
#!/bin/bash if [ $# != 2 ];then echo "bash $0 old_hostname new_hostname" exit 0 fi old_hos ...
- 【07月03日】A股ROE最高排名
个股滚动ROE = 最近4个季度的归母净利润 / ((期初归母净资产 + 期末归母净资产) / 2). 查看更多个股ROE最高排名 兰州民百(SH600738) - ROE_TTM:86.45% - ...
- c# 枚举类型怎么用?
有很多将枚举类型的都没有说详细...所以我这里贴出来一下,免得我忘记.................................. using System; using System.Coll ...
- Keras 使用过程问题汇总
以下是Keras 使用过程出现的一些问题: (1)Keras 后端选择问题 一开始是选用的Theano,结果迭代一轮所花时间很长: 后面改用:TensorFlow作为后端,结果果然变快了: 改完Ten ...
- C++17 新特性之 std::optional(上)
最近在学习 c++ 17 的一些新特性,为了加强记忆和理解,把这些内容作为笔记记录下来,有理解不对的地方请指正,欢迎大家留言交流. 引言 在介绍之前,我们从一个问题出发,C++ 的函数如何返回多个值? ...
- day61——多表操作(增、删除、改、基于对象的跨表查询)
day61 增删改查 增加 # 增加 # 一对一 # au_obj = models.AuthorDetail.objects.get(id=4) models.Author.objects.crea ...