记由于mongoDB主从同步机制导致的一次线上bug
场景
有一个业务逻辑是这样的,从页面中导入数据,之后会先将该数据写入操作记录的临时表中,之后使用异步的方式对导入的数据执行匹配
现象
在线上环境,偶尔会出现匹配完成之后丢失部分数据或者丢失全部数据的情况
解决
因为是一个偶现的问题,于是在开发环境中根本没有办法复现此种情况,因为这是一个高IO密集型的任务,所以程序中使用了异步并采用多线程方式执行。一开始猜想可能是多线程操作的时候出了问题,于是对多线程的逻辑进行各种优化和调整。
具体优化如下:在低数据量集的时候使用单线程处理,避免线程切换带来的额外开销。在数据量较大时采用分段的形式将数据量平均分为N份,每份添加一个线程进行处理,避免了大量数据及并发情况下,任务大量积压在线程池阻塞队列中无法执行的情况。
解决方案1经过上述的优化之后,确实提升了该任务的执行效率以及资源利用情况。但是上述的bug问题并没有被解决。于是可以排除是因创建大量线程/或大量线程积压在阻塞队列中引发的异常问题。
经过进一步排查,发现其实并不是程序代码导致的该问题,通过观察mongoDB机器的负载情况,偶然出现上述bug情况时,多是因为其他程序执行了高IO任务,占据了大量的资源。
于是猜想,应该是mongoDB主从同步时,因为其他操作导致性能较差,于是同步的速度很慢,进而导致主从数据不一致的情况。
这里简单介绍一下mongoDB的master-slave模式,该模式核心其实是replication,通过建立多个mongoDB节点(一master多slave),master节点向外提供写请求,slave节点负责读请求,来达到读写分离提升性能,并达到高可用的目的。其中master节点写了数据之后, 会异步的将数据同步到其他slave节点。
mongoDB主从模式示意图其实这是典型CAP问题,一致性(Consistency),可用性(Availability),分区容忍性(Partition tolerance)三者只能取其二,mongoDB的主从复制模式,实际上是取了A和P而放弃了C,仅仅保证最终一致性。其实无论负载如何, 数据不一致的延迟的是一定存在的,不过是时间长短而已。而上述业务场景中需要数据的一致性,来保证进行匹配的时候能拿到所有写进入的数据。
这里其实可以通过在程序中sleep或者引入重试机制来等待mongoDB进行消息的同步。
但是这种处理方式其实并不好,mongoDB实际上有处理该问题的API,{w: "majority"},即写的时候阻塞到写到大多数结点写完才算完成。有了这点还是不够的,因为你要读的从结点并不能保证一定在“大多数”之内。为了保证读结点在“大多数”之内{readConcern: "majority"}——多数结点有的才算有。但是这样的话一个请求要压在大多数节点上,违背了读写分离,分散数据库压力的初衷,而且也将写操作趋近于同步,影响性能。 于是单独搞了一个可以保证数据一致性的connection,以便需要数据一致性的时候使用,而其他操作则不使用该操作。