MongoDB之写关注及PSA架构相关问题

MongoDB的写关注主要指定了写入操作的确认级别。具体来讲就是客户端在向MongoDB执行比如delete、insert、update等相关写入操作时,MongoDB的写入行为,比如是否立刻刷盘、是否需要等待Secondary节点确认等。

写关注说明

写关注配置主要包含以下3个字段:

{ w: , j: , wtimeout:  }
  • w选项指定了写入操作需要等待确认的副本集成员个数。取值如下:
    • “majority”:写入操作需要等待副本集中的多数派节点(不包含仲裁节点)确认日志写入。比如3节点的副本集,写入操作需要等待主节点和其中一个从节点确认日志写入。
    • “数字”:指定写入操作需要等待副本集中确认日志写入的节点数。比如“w:1”要求主节点写入;“w:2”要求副本集中至少2个成员确认写入日志。
  • j选项指定了写入操作是否需要等待日志刷盘
    • 为true时当数据写入时会立即刷盘其相应的日志。w选项相关的写入确认机制为确认日志已刷盘。
    • 为false时不要求日志刷盘。w选项相关的写入确认机制为确认日志已写入到内存中。
  • wtimetout指定了写入操作等待超时时间,防止客户端被持续阻塞下去。如果发生超时,会返回给客户端错误,但是并不代表数据没有写入到主节点,已经写入到主节点的数据也不会被回滚。

以下示例为设置w为3,但实际只有两个节点可写,当写入时,最终等待超时的情况:

PRIMARY> use db
switched to db db
PRIMARY> show tables;
PRIMARY> db.foo.insert({"name":"lisa"},{writeConcern:{w:3,wtimeout:5000}})
WriteResult({
        "nInserted" : 1,
        "writeConcernError" : {
                "code" : 64,
                "codeName" : "WriteConcernFailed",
                "errInfo" : {
                        "wtimeout" : true
                },
                "errmsg" : "waiting for replication timed out"
        }
})
PRIMARY> db.foo.find()
{ "_id" : ObjectId("66cc79b22ad2d8af73b13352"), "name" : "lisa" }

可以看到,当数据承载节点少于写关注要求时,会进行超时等待,超时之后MongoDB会返回错误,但实际已经写入到活跃节点的数据不会回滚。

设置默认写关注

setDefaultRWConcern命令可以设置默认读关注和写关注,设置默认写关注示例,返回结果为修改后的全局读写关注:

test> use admin
switched to db admin
admin> db.adminCommand(
...   {
...     setDefaultRWConcern : 1,
...     defaultWriteConcern: { w:"majority",wtimeout:1000 },  // 全局默认设置
...     writeConcern: { w:1,wtimeout:5000 },         // 设置当前会话
...   }
... )
{
  defaultReadConcern: { level: 'local' },
  defaultWriteConcern: { w: 'majority', wtimeout: 10 },
  updateOpTime: Timestamp({ t: 1724723294, i: 2 }),
  updateWallClockTime: ISODate("2024-08-27T01:48:14.987Z"),
  defaultWriteConcernSource: 'global',
  defaultReadConcernSource: 'implicit',
  localUpdateWallClockTime: ISODate("2024-08-27T01:48:14.987Z"),
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1724723294, i: 3 }),
    signature: {
      hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
      keyId: Long("0")
    }
  },
  operationTime: Timestamp({ t: 1724723294, i: 3 })
}

使用getDefaultRWConcern命令查看默认读写关注

admin> db.adminCommand({"getDefaultRWConcern": 1})
{
  defaultReadConcern: { level: 'local' },
  defaultWriteConcern: { w: 'majority', wtimeout: 10 },
  updateOpTime: Timestamp({ t: 1724723294, i: 2 }),
  updateWallClockTime: ISODate("2024-08-27T01:48:14.987Z"),
  defaultWriteConcernSource: 'global',
  defaultReadConcernSource: 'implicit',
  localUpdateWallClockTime: ISODate("2024-08-27T01:48:14.987Z"),
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1724723578, i: 1 }),
    signature: {
      hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
      keyId: Long("0")
    }
  },
  operationTime: Timestamp({ t: 1724723578, i: 1 })
}

或者在副本集配置中查看副本集的写关注确认节点数量:

test> conf=rs.config()
test> conf.settings.getLastErrorDefaults
{ w: 1, wtimeout: 0 }

数据安全性及性能的平衡

通过上面的介绍我们知道写writeConcern的w选项指定了写入操作需要等待确认的副本集成员个数;j选项指定了写入操作是否需要等待日志刷盘。那么在w设置为1、j设置为false的情况下肯定是效率最高的,但是也是最容易丢失数据的;当w设置为当前数据承载节点个数、j设置为true时数据是最安全的,因为每次写入都需要确认所有的节点都已经完全写入,不会发生数据丢失或回滚的情况,但是只要其中一个节点发生故障,整个集群的写入就会发生问题。所以我们通常建议将w设置为majority,j设置为true来平衡数据安全性和性能。当然,对于不同业务场景也可以进行更灵活的设置,比如,如果我们的数据全都是一些不是很重要的日志数据,即使数据丢失一部分也没有太大的关系,哪我们的目标就是只用追求性能就好了。

与之关联的PSA架构的问题

通过上面的SQL演示示例可知,当数据承载节点小于写关注要求时,数据写入就需要等待超时,我们知道,在PSA架构下,仲裁节点是不参与承载数据的,只参与选举操作,那么假设我们为了保证数据安全性将w选项设置为majority,即写入操作需要得到两个承载数据节点的写入确认,如果我们的Primary节点发生了故障,这时候仅有Secondary从节点就会被选举为Primary,但是由于失去了一个数据承载节点,此时仅存的新Primary数据节点已经不能满足写关注的majority要求,就会产生写入等待超时的问题,可以此时业务写入就出现了问题,也就是说整个结构就完全失去了高可用功能。当然,PSA架构还不仅仅只有这一个问题,但是从这个角度来讲,PSA架构已经不适合我们在生产环境中使用。

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