MongoDB Replication
基础知识
复制意味着自动维护分布在多个MongoDB服务器上的数据。
复制是作为一种称为副本集(replica sets)的机制实现的。
副本集是一组节点,这些节点被配置为可以在节点消失时自动同步其数据并进行故障切换。
旧版本的MongoDB支持一种称为master-slave的复制方法(现在被认为已弃用),该方法仍然可以在mongodbv3.0中使用。
在这两种方法中,单个主节点接收所有写操作,然后所有次节点异步读取并将这些写操作应用到自己。
副本集使用与主从相同的复制机制,并确保自动故障切换。
在副本集方法中,如果主节点脱机,则只要有可能,就会自动将其中一个次节点提升为主节点。
此外,副本集提供了对以前复制方法的改进,例如更容易恢复和更复杂的部署拓扑。
在副本集方法中,只有将数据写入大多数成员节点(即超过50%的服务器)后,数据才会被视为已提交。
这意味着,如果一个副本集只有两个服务器,则没有服务器可以关闭。
如果副本集中的主节点在复制其数据之前发生故障,则其他成员将继续接受写操作,并且必须回滚任何未复制的数据,这意味着无法再读取该数据。
为什么我们要复制?
复制提供了安全机制,保护数据库系统免受环境故障的影响,例如:
- 应用程序和数据库之间的网络连接丢失。
- 电源丢失(停电)。
- 持久存储设备(HDD、SSD)故障。
除了防止外部故障外,复制对于系统的持久性也很重要。
在没有备份/日志记录的情况下运行时,无法轻松恢复损坏数据的原始值。
如果单个节点由于硬件故障而关闭,复制始终可以保证数据文件的干净副本。
复制还促进了冗余、故障切换、维护和负载平衡。(复制主要是为冗余而设计的)。
复制确保了复制节点的内容与主节点同步
副本可以位于与主节点相同的位置,也可以位于通过广域网与主节点连接的不同位置。
复制是异步的,因此任何类型的网络延迟或节点之间的分区都不会影响主节点的性能。
对已复制节点的内容的修改可以在主节点之后延迟恒定的秒数、分钟数甚至小时数。
如果主节点的内容已损坏,则有机会“撤销”。
例如,如果有人意外地丢弃了一个错误的对象集合,并且副本“在时间上被延迟”,那么就可以从副本节点进行恢复。
延迟的复制副本为管理员提供了反应和恢复数据的时间。
复制的另一个应用是故障转移,即主节点发生故障,其中一个冗余节点充当主节点的角色
在MongoDB中,这样的“切换”是自动执行的。
复制允许在生产时在主节点以外的节点上执行高工作负载操作,从而简化了维护。
例如,可以在辅助节点上运行备份,以避免主节点上不必要的额外负载,并避免停机。
复制允许在主节点上的操作同时在辅助节点上构建大型索引。
然后,可以将辅助节点与现有的主节点交换,然后在新的辅助节点上再次构建相同的索引。
复制允许跨多个副本平衡读操作。
数据可以同时从许多单独的副本读取;这是扩展系统的最简单的方法。
复制模拟
我们使用复制集创建两个复制节点,由端口4000和4001上的两个Mongo进程模拟。
启动终端并处理以下shell命令:
cd ~
mkdir node0
mkdir node1
在终端窗口启动mongod服务器,数据位于node0,监听端口4000并连接到复制集“rs0”
mongod --dbpath node0 --port 4000 --replSet "rs0"
打开另一个终端窗口并启动mongod服务器,其中node1中的数据侦听端口4001,并附加到相同的复制集“rs0”。
前面那个终端窗口不要关闭!
mongod --dbpath node1 --port 4001 --replSet "rs0"
启动终端,然后启动mongo客户端,连接到侦听端口4000的服务器:
mongo --port 4000
在mongo客户端显示 > 后处理以下命令:
rs.initiate()
使当前节点成为PRIMARY:
rs.conf()
添加4001处的mongod服务器作为辅助节点:
rs.add("localhost:4001")
查看状态:
rs.status()
打开另一个终端窗口并连接到mongod服务器 4001:
mongo --port 4001
设节点为slave:
rs.slaveOk()
现在,为了测试复制集,我们将创建一个新的集合,并在端口4000的mongo服务器上插入一个新文档;复制机制将插入复制到端口4001的mongod服务器。
返回到mongo客户端以4000端口连接mongod服务器的窗口,处理以下命令:
use JFM
db.names.insert({"full-name":"ABC"})
db.names.find()
移动到一个窗口,其中mongo客户端通过端口4001连接到mongod服务器,并执行命令:
use JFM
db.names.find()
由于服务器处于slave模式,连接到端口4001的mongod服务器的客户端试图插入新文档失败.
要关闭两个复制服务器,请在两个窗口中执行以下语句:
use admin
db.shutdownServer()
在创建复制集并关闭复制集之后,可以按照以下方式重新启动复制集。
在终端窗口启动mongod服务器,数据位于node0,监听端口4000并连接到复制集“rs0”:
mongod --dbpath node0 --port 4000 --replSet "rs0"
打开另一个终端窗口并启动mongod服务器,其中node1中的数据侦听端口4001,并连接到相同的复制集:
mongod --dbpath node1 --port 4001 --replSet "rs0"
打开另一个终端窗口并连接到mongod 4001服务器
mongo --port 4001
使这个节点变成 slave。
rs.slaveOk()
启动终端,然后启动mongo客户端,连接到侦听端口4000的服务器:
mongo --port 4000
返回到mongo客户端以4000端口连接mongod服务器的窗口,处理以下命令:
use JFM
db.names.insert({"full-name":"DEF"})
db.names.find()
切换到mongo客户端通过端口4001连接到mongod服务器的窗口,执行命令:
use JFM
db.names.find()
你会发现模拟复制成功了。
References
- Banker K., Bakkum P., Verch S., Garret D., Hawkins T., MongoDB in Action, 2nd ed., Manning Publishers, 2016.
- MongoDB Manual, Replication https://docs.mongodb.com/v3.6/replication/