一. 背景
最近 MongoDB 群里面有群友遇到2次重启 MongoDB 后一直处于实例恢复状态(应用 OPLOG ),多达几天甚至更长才完成重启,通常 MongoDB 副本集三个实例作为标准,重启主库会发生重新选出新主节点(通常在12s内完成)重新对外服务,通常不符合官方标准化或者内部发生异常导致的。经过了解副本集采用 PSA 架构且存在一个数据从节点不可达的情况(甚至有的从节点宕机几个月没有发现),来分析这些情况以及如何应对。
主要包括以下内容:( WT 存储引擎下版本是3.2、3.4、3.6、4.0、4.2为主,4.4、5.0也存在)。
PSA 架构下从节点宕机后,重启主库为什么会这么久
PSA 架构还有哪些问题
PSA 架构下如何缓解内存压力以及推荐 PSS 方案
模拟 PSA 架构下重启主库实例后长时间等待的情况并通过不同方案来解决
二. PSA 架构重启主库为什么会这么久
备注:有个提前是从库宕机存在一定周期且在此期间产生大量的脏数据。
2.1 官方 PSA 架构介绍
当数据节点宕机且 enableMajorityReadConcern ( WT 3.2版本开始默认开启),内存压力增大,这里只是说内存压力增大,没有进一步说明影响,比如说压力超过内存限制后如何处理、对性能的影响以及超长时间等待重启之类。(由于没有源码能力,相关东西只能通过 jira 、专家交流以及自己实验来验证)。
2.2 为什么会造成主库内存压力?
用于从副本集或者集群中读取数据时,能够允许读取到被大多数节点收到并被确认的数据,对应关系型数据库里面提交读。
注意:这里只是允许而已(数据库提供的能力,3.2版本才开始支持)。
客户端是否需要根据实际 readConcern 级别,不管我们是否需要这种 majorityReadConcern 级别,数据库 WT 引擎已经维护这些信息在内存中(默认是开启)。
当 PSA 副本集中存在一个数据节点宕机时,主库内存中数据的 Majority commit point 是无法推进的,此时 checkpoint 是不能将这些数据持久化(内存中脏数据无法更新到数据文件中),同时 OPLOG 会保留所有变更操作,如果从库宕机时间长且主库很忙,OPLOG 会增长很大(4.0版本开始会超过配置大小)。
PSA 此时 checkpoint 不能对 Majority commit point 后的数据进行持久化,主库必须维护最近 Majority commit point 的快照提供给读,所以内存压力会增大和内存使用,最终这些内存数据溢出,此时 MongoDB 利用 SWAP 技术将内存中置换到磁盘上(将内存数据置换到磁盘上 WiredTigerLAS.wt ),所以性能会下降,如从库宕机时间长,此时主库性能也慢,同时磁盘空间也会暴涨。可能会考虑重启实例(通常情况下重启能解决大部分问题),那么实例可能重启需要等特几天甚至更长时间才能完成。因为数据没有持久化,重启的话就需要进行实例恢复,那么就会出现开头说重启好多天都没有完成的悲剧。重启过程这个问题会被无限循环。(4.4开始重构来缓解这个问题,使用 WiredTigerHS.wt 来替代)。最坏的情况可能会导致实例 OOM 。
三. PSA 架构还存在哪些问题
PSA 相比 PSS 少一份副本数据,相对应就 cost down. 这个是最直接好处。例如三机房部署 PSA 架构的副本集或者分片,对应 A 的机器最低配即可,不需要消耗什么资源。通常三机房采用 PSSSA 或者 PSSSS ,发生故障时优先切换本地机房。
正常情况下 PSA 正常下运行与 PSS 架构下无差别。主要出现 S 节点不可达以及长延迟情况会存在异常,除了内存压力增大造成性能的影响以及跟超长等待时间重启外,还如下常见场景:
writeConcern 或者 readConcern 为 majority ,读写会异常。majority 表示数据被副本集成员中大多数节点收到并被主确认。5.0之前版本默认 w:1,表示被主节点确认后表示操作成功,此时此群出现故障可能会导致写入主节点被回滚,从而造成数据丢失。所以 w:majority 是保证集群数据故障时不丢失的必需配置。
那么 majority 到底是多少个节点?对于 majority 是怎么计算?为什么 PSA 架构下宕机一个数据节点就不满足 majority ?
majority 节点数=最小值(副本集中所有数据节点具有的投票能力总数与副本集中1加上取整(1/2的具备投票节点总数包括仲裁节点))。默认情况下 PSA 中所有节点都具备投票能力,那么此时 majority 节点数= min (2,3(1+取整(0.5*3))) =2。从4.2.1版本开始,可以通过 rs.status() 中 writeMajorityCount 、 majorityVoteCount 来看。如果此时宕机一个数据节点或者不可达时,此时 majority 还是2,不会因为状态的改变而减少 majority 个数。此时需要满足 w:majority 的操作要不超时要不永不返回的状态。
注意:此时数据已经写入主节点,不管是超时还是永不返回,数据不会被回滚(不考虑事务的场景以及 failover 情况)。
同理3.2版本开始默认开启 enableMajorityReadConcern ,此时 majority commit point 也不会被推进。所以说 PSA 在一定程度上通过节约成本来降低系统高可用性。当然,说没有显式开启 majority ,是不是就没有问题?当存在一个数据节点不可达时,有些潜在场景默认是 majortiy 配置且不能修改,例如5.0开始 enableMajorityReadConcern 这个不能被禁用。例如 changestream 要求数据被大多数节点应用,同时也影响分片集群部分功能。
集群部分功能异常
分片集群数据平衡,源或者目标分片中不能满足大多数成员时,数据平衡或者扩缩分片都会失败。分片集群管理,例如 shardCollection 、 dropIndex 等要求 majoriy 都会失败。分片集群下 changeStream 同样会无法捕获最新数据造成同步延迟。
隐藏丢失数据操作
如果从库已经宕机 N 时间,此时主库也宕机了,如果运维人员先启动老的从库,那么会"丢 N 时间"数据,这个数据存在在原主库,此时原主库启动后需要先回滚 N 时间数据才能重新加入到副本集中,通常回滚有限制,大概率会回滚失败。
四. 如何缓解内存压力
4.1 缓解内存压力
避免一个数据节点实例宕机情况下对系统的影响,通过完善的监控及时发现节点异常(宕机、延迟),及时处理故障,否则无能为力。
禁用 MajorityReadConcern ( PSA 架构来避免内存压力,同时注意 changeStream ,4.2版本不管这个参数,对于出现问题的集群或者副本集。
临时将异常从库的优先级别与投票都设置为0(5.0版本由于不能禁用 MajorityReadConcern ,注意这个只能修改下应对从库宕机或延迟时,来缓解主库内存压力以及解决一些配置 majority 场景,但失去高可用,因为从库不能被选为主。适用场景是数据库需要重启时存在大量脏数据刷盘或者应用配置 w:majority 时,修改宕机实例优先级别与投票为0后进行重启才可以,如果已经重启的实例,此时只能等待)。
总结: PSA 解决从库宕机后如何缓解主库内存压力,通过有效监控及时消除故障点,如果没有及时发现,在重启前通过方案3来避免长时间重启问题,针对方案2需要提前规划好,但对于 majority 场景以及分片模式下操作还是无能为力,如果从库宕机很久,此时已强制重启主库,此时只能进入躺平状态去等。其他解决方案需要具体问题具体分析,例如只要系统能写入数据即可,可以把从实例恢复起来或者搭建空实例。
4.2 推荐方案
尽管通过禁用参数或者修改配置来缓解问题,但存在潜在的问题或者不熟悉的人还是会遇到同样问题,条件允许情况下,应使用 PSS 取代 PSA 架构能够解决单一数据节点宕机带来的影响。如果正在使用 PSA 架构也没有关系,知道存在问题即可,出现问题能够知道带来的影响是什么即可。
五. 场景模拟解决方案
备注:搭建4.2 PSA 副本集,手动 S 实例关闭并通过 POC 压测数据,构造20个字段1.1亿表。
5.1 查看数据情况
亿 show dbs 显示 POCDB 为0,这个显示不合理。datasize:62G ,磁盘上大小为12K (这个说明数据并没有写入到磁盘)。
PRIMARY:[db]POCDB> show dbs
POCDB
POCDB 0.000GB
admin 0.000GB
config 0.001GB
local 36.804GB
[db]POCDB> db.POCCOLL.count();
115155968
[db]POCDB>db.POCCOLL.stats();
{
"ns"