MongoDB复制集

文章目录

  • 一、介绍
    • 1、存在的意义和作用?
    • 2、需要实现啥功能?
    • 3、典型案例
    • 4、注意事项
  • 二、搭建
    • 1、安装MongoDB,配置环境变量
    • 2、创建数据目录
    • 3、配置文件
    • 4、启动 MongoDB 进程
    • 5、配置复制集
    • 6、验证
  • 三、写策略writeConcern
    • 1、w参数
    • 2、j参数
      • 2.1 介绍
      • 2.2 可选的参数值
      • 2.3 实战
    • 3、注意事项
  • 四、读策略
    • 1、readPreference
      • 1.1 可选的值
      • 1.2 适用场景
      • 1.3 Teg
      • 1.4 配置
      • 1.5实战
    • 2、readConcern
      • 2.1 可选的值
      • 2.2 majority
      • 2.3 snapshot

一、介绍

1、存在的意义和作用?

主要意义在于实现服务高可用

作用

  • 高可用:主要作用
  • 数据分发:将数据从一个区域复制到另一个区域,减少另一个区域的读延迟
  • 读写分离:不同类型的压力分别在不同的节点上执行
  • 异地容灾:在数据中心故障时候快速切换到异地

2、需要实现啥功能?

  • 数据写入时将数据迅速复制到另一个独立节点上
  • 在接受写入的节点发生故障时自动选举出一个新的替代节点

数据是如何复制的?

  • 当一个修改操作,无论是插入、更新或删除,到达主节点时,对数据的操作将被记录下来(经过一些必要的转换),这些记录称为oplog
  • 从节点通过在主节点上打开一个tailable游标不断获取新进入主节点的oplog,并在自己的数据上回放,以此保持跟主节点的数据一致

如何通过选举完成故障恢复?
复制集中最多可以有50个节点,但具有投票权的节点最多7个

  1. 具有投票权的节点之间两两互相发送心跳(2s)
  2. 当5次心跳未收到时判断为节点失联(如果失联的是主节点,从节点会发起选举,选出新的主节点;如果失联的是从节点则不会产生新的选举)
  3. 选举基于 RAFT一致性算法实现,选举成功的必要条件是大多数投票节点存活

影响选举的因素?

  • 集群存活节点大于一半
  • 被选举为主节点的节点需要具备的条件:能够与多数节点建立连接、具有较新的 oplog、具有较高的优先级

常见选项配置?

  • 是否具有投票权(v 参数):有则参与投票
  • 优先级(priority 参数):优先级越高的节点越优先成为主节点
    优先级为0的节点无法成为主节点
  • 隐藏(hidden 参数):复制数据,但对应用不可见。隐藏节点可以具有投票仅,但优先级必须为0
  • 延迟(slaveDelay 参数):复制 n 秒之前的数据,保持与主节点的时间差

3、典型案例

1主2从,最简单的复制集
备注:复制集需由3个及以上的具有投票权的节点组成

不同的节点功能?
主节点:接收写入操作,选举时投票
从节点:复制主节点上的新数据,选举时投票
投票节点:只负责投票,不存储数据,一般不推荐使用

4、注意事项

硬件的话,节点的配置必须一致,保证地位一样,另外一个就是要独立,保证不会同时宕机
软件的话,节点的版本必须一致,避免出现不兼容问题
增加节点不会增加系统写性能,可能降低写性能,这是因为需要同步,但会提高读性能

二、搭建

1、安装MongoDB,配置环境变量

这个不展开,之前介绍过,可以看入坑篇

2、创建数据目录

为3个复制集节点创建各自的数据目录,分别为data1、data2、data3

  • Linux
    mkdir -p /data/db{1,2,3}
  • Windows
    md D:\mongodb\data\db1
    md D:\mongodb\data\db2
    md D:\mongodb\data\db3

3、配置文件

复制集的每个mongod进程应该位于不同的服务器,这里我们用一台机器,要弄3个进程,配置不同的端口号即可

systemLog:
destination: file
path: D:\mongodb\data1\mongod.log # 日志文件路径
logAppend: true
storage:
dbPath: D:\mongodb\data1 # 数据目录
net:
bindIp: 0.0.0.0
port: 28017 # 端口
replication:
replSetName: forlan0

说明:

bindIp为什么是0.0.0.0?适用所有网卡,都可以访问该服务
fork表示以后台进程运行,linux可以配置
这里只展示了1台,其它两个主要改端口号和数据、日志路径

4、启动 MongoDB 进程

mongod -f D:\mongodb\data\db1\mongod.conf
mongod -f D:\mongodb\data\db2\mongod.conf
mongod -f D:\mongodb\data\db3\mongod.conf
因为 Windows 不支持 fork,以上命令需要在3个不同的窗口执行,执行后不可关闭窗口否则进程将直接结束

5、配置复制集

  • 方法1
rs.initiate()
rs.add("centosvm:28018")
rs.add("centosvm:28019")

centosvm是hostname

  • 方法2
rs.initiate({_id: "forlan0",members: [{_id: 0,host: "localhost:28017"},{_id: 1,host: "localhost:28018"},{_id: 2,host: "localhost:28019"}]})

6、验证

主节点写入

db.test.insert({ a:1 })

从节点读取

rs.slaveOk()
db.test.find()

三、写策略writeConcern

决定一个写操作落到多少个节点上才算成功

1、w参数

  • 0:发起写操作,不关心是否成功
  • 1~n:写操作需要被复制到指定节点数才算成功
    n为集群最大数据节点数;1的话,表示数据写入到Primary就向客户端发送确认,从节点采取异步线程去写
  • majority:写操作需要被复制到大多数节点上才算成功
    发起写操作的程序将阻塞,直到写操作到达指定的节点数为止
    大多数节点确认模式
    起码写到一个从节点才返回
  • all:全部节点确认模式,可能长时间阻塞

2、j参数

2.1 介绍

Journaling,类似于关系数据库中的redolog,能够使MongoDB数据库由于意外故障后快速恢复,由于提交journal日志会产生写入阻塞,所以它对写入的操作有性能影响,但对于读没有影响

MongoDB2.4版本后默认开启了Journaling日志功能,mongod实例每次启动时都会检查journal日志文件看是否需要恢复

2.2 可选的参数值

  • true:写操作落到 journal 文件中才算成功
  • false:写操作到达内存即算作成功
    MongoDB复制集_第1张图片
    对于5个节点的复制集来说,写操作落到多少个节点上才算是安全的?
    至少3个节点或者配置为majority

2.3 实战

1、测试writeConcern参数

db.test.insert( {count: 1}, {writeConcern: {w: "majority"}})
db.test.insert( {count: 1}, {writeConcern: {w: 3 }})
db.test.insert( {count: 1}, {writeConcern: {w: 4 }})

2、配置延迟节点,模拟网络延迟(复制延迟)

conf=rs.conf()
conf.members[2].secondaryDelaySecs = 5   //老版本可能是conf.members[2].slaveDelay = 5
conf.members[2].priority = 0 // 不具备选举权限
rs.reconfig(conf)

观察复制延迟下的写入:执行插入操作,设置timeout参数,超时报错

db.test.insert( {count: 1}, {writeConcern: {w: 3, wtimeout:3000 }})

3、注意事项

  • 虽然多于半数的 writeConcern 都是安全的,但通常只会设置 majority,因为这是等待写入延迟时间最短的选择
  • 不要设置 writeConcern 等于总节点数,因为一旦有一个节点故障,所有写操作都将失败
  • writeConcern 虽然会增加写操作延迟时间,但并不会显著增加集群压力,因此无论是否等待,写操作最终都会复制到所有节点上
    设置 writeConcern 只是让写操作需要等待复制完成后,再返回而已
  • 重要数据应用设置{w: “majority”},以确保数据不丢失
  • 普通数据应用 设置{w: 1} ,以确保最佳性能

四、读策略

1、readPreference

解决的是,从哪里读,决定使用哪一个节点来满足正在发起的读请求

1.1 可选的值

  • primary: 只选择主节点(默认值);
  • primaryPreferred:优先选择主节点,如果不可用则选择从节点;
  • secondary:只选择从节点;
  • secondaryPreferred:优先选择从节点,如果从节点不可用则选择主节点;
  • nearest:选择最近的节点;

指定 readPreference 时也应注意高可用问题,例如将 readPreference 指定 primary,则发生故障转移不存在没有节点可读。如果业务允许,则应选择 primaryPreferred

1.2 适用场景

  • primary/primaryPreferred:用户下订单后马上将用户转到订单详情页(查询时效性要求)
  • secondary/secondaryPreferred:用户查询自己下过的订单(查询历史订单对时效性通常没有太高要求)
  • secondary:生成报表,对时效性要求不高,但资源需求大,可以在从节点单独处理,避免对线上用户造成影响
  • nearest:将用户上传的图片分发到全世界,让各地用户能够就近读取(每个地区的应用选择最近的节点读取数据)

1.3 Teg

作用?
因为readPreference 只能控制使用一类节点,而它可以选择控制到一个或几个节点

场景?
一个 5 个节点的复制集:

  • 3 个节点硬件较好,专用于服务线上客户
  • 2 个节点硬件较差,专用于生成报表

所以可以使用 Tag 来达到这样的控制目的,在线应用读取时指定{purpose: “online”},报表读取时指定 {purpose: “analyse”}

1.4 配置

  • 通过 MongoDB 的连接串参数
    xxx?replicaSet=rs&readPreference=secondary
  • 通过 MongoDB 驱动程序 API
    MongoCollection.withReadPreference(ReadPreference readPref)
  • Mongo Shell
    db.collection.find({}).readPref(“secondary”)

1.5实战

主节点写入 {x:1}, 观察该条数据在各个节点均可见

db.test.drop()
db.test.insert({x:1})

在两个从节点分别执行 db.fsyncLock() 来锁定写入(同步)

db.fsyncLock()

主节点写入 {x:2}

db.test.insert({x:2})

主节点上看到有2条数据
从节点上看到只有1条数据
解除从节点锁定 db.fsyncUnlock()
在从节点上看,可以看到同步的数据了

db.test.find().readPref("secondary")

2、readConcern

解决的是,什么数据可以读

决定这个节点上的数据哪些是可读的,类似于关系数据库的隔离级别

2.1 可选的值

有事务要求,使用snapshot,否则使用majority

  • available
    读取所有可用的数据
  • local
    读取所有可用且属于当前分片的数据
  • majority
    读取在大多数节点上提交完成的数据
  • linearizable
    可线性化读取文档
  • snapshot
    读取最近快照中的数据

2.2 majority

只读取大多数据节点上都提交了的数据
如何实现?
节点上维护多个 x 版本,MVCC 机制,通过维护多个快照来链接不同的版本

  • 每个被大多数节点确认过的版本都将是一个快照
  • 快照持续到没有人使用为止才被删除

实战:
1、安装 3 节点复制集(一主两从)
2、配置文件内 server 参数 enableMajorityReadConcern
MongoDB复制集_第2张图片
3、将复制集中的两个从节点使用 db.fsyncLock() 锁住写入(模拟同步延迟)

db.test.save({"x":1})
db.test.find().readConcern("local")
db.test.find().readConcern("majority")

解锁后就正常了

结论:
写操作在没到达大多数节点前,主节点挂了,写操作就丢失了,操作回滚,可以理解为事务,只不过是大多数节点成功才提交,所以可以避免脏读

2.3 snapshot

只在多文档事务中生效,类似隔离级别,不可重复读

测试:
1、准备数据

db.tx.insertMany([{ x: 1 }, { x: 2 }]);

2、事务操作

var session = db.getMongo().startSession();
session.startTransaction();
var coll = session.getDatabase('test').getCollection("tx");
coll.updateOne({x: 1}, {$set: {y: 1}});
coll.findOne({x: 1});
db.tx.findOne({x: 1});
session.abortTransaction();

事务内操作db.tx.findOne({x: 1}),得到值 {x:1, y:1}
事务外操作db.tx.findOne({x: 1}),得到值 {x:1}

3、可重复读Repeatable Read,事务操作

var session = db.getMongo().startSession();
session.startTransaction({readConcern: {level: "snapshot"},writeConcern: {w: "majority"}});
var coll = session.getDatabase('test').getCollection("tx");
coll.findOne({x: 1}); 
db.tx.updateOne({x: 1}, {$set: {y: 1}});
db.tx.findOne({x: 1});
coll.findOne({x: 1}); 
session.abortTransaction();

两次读取结果一样,如下:
MongoDB复制集_第3张图片

注意事项

  • 事务默认必须在 60 秒(可调)内完成,否则将被取消
  • 涉及事务的分片不能使用仲裁节点
  • 事务会影响 chunk 迁移效率。正在迁移的 chunk 也可能造成事务提交失败(重试即可)
  • 多文档事务中的读操作必须使用主节点读
  • readConcern 只应该在事务级别设置,不能设置在每次读写操作上

你可能感兴趣的:(数据库,mongodb,数据库)