mongodb每秒写入量_OPPO百万级高并发MongoDB集群性能数十倍提升优化实践

本文详细介绍了在不增加硬件资源的情况下,针对MongoDB集群进行的软件优化,包括业务层面调整、MongoDB配置优化和WiredTiger存储引擎优化,实现了每秒写入量的数十倍提升,有效降低了平均时延和系统负载。
摘要由CSDN通过智能技术生成

本文是“我和MongoDB的故事”MongoDB征文比赛的一等奖文章,杨亚洲也到2019年MongoDB中文社区年终盛会进行了现场分享。接下来我们一起来欣赏下完整版内容。

1 背景

线上某集群峰值TPS超过100万/秒左右(主要为写流量,读流量较少,读写流量做了主从读写分离,读流量走从节点,qps数百上千),峰值tps几乎已经到达集群上限,同时平均时延也超过100ms,随着读写流量的进一步增加,时延抖动严重影响业务可用性。该集群采用mongodb天然的分片模式架构,数据均衡的分布于各个分片中,添加片键启用分片功能后实现完美的负载均衡。集群每个节点流量监控如下图所示:

从上图可以看出集群流量比较大,峰值已经突破120万/秒,其中delete过期删除的流量不算在总流量里面(delete由主触发删除,但是主上面不会显示,只会在从节点拉取oplog的时候显示)。如果算上主节点的delete流量,峰值总tps超过150万/秒。

2 软件优化

在不增加服务器资源的情况下,首先做了如下软件层面的优化,并取得了理想的数倍性能提升:

业务层面优化

Mongodb配置优化

存储引擎优化

2.1 业务层面优化

该集群总文档近百亿条,每条文档记录默认保存三天,业务随机散列数据到三天后任意时间点随机过期淘汰。由于文档数目很多,白天平峰监控可以发现从节点经常有大量delete操作,甚至部分时间点delete删除操作数已经超过了业务方读写流量,因此考虑把delete过期操作放入夜间进行,过期索引添加方法如下:

Db.collection.createIndex( { “expireAt”: 1 }, { expireAfterSeconds: 0 } )

上面的过期索引中expireAfterSeconds=0,代表collection集合中的文档的过期时间点在expireAt时间点过期,例如:

db.collection.insert( {

//表示该文档在夜间凌晨1点这个时间点将会被过期删除

“expireAt”: new Date(‘July 22, 2019 01:00:00’),

“logEvent”: 2,

“logMessage”: “Success!”

} )

通过随机散列expireAt在三天后的凌晨任意时间点,即可规避白天高峰期触发过期索引引入的集群大量delete,从而降低了高峰期集群负载,最终减少业务平均时延及抖动。

Delete过期Tips1: expireAfterSeconds含义

在expireAt指定的绝对时间点过期,也就是12.22日凌晨2:01过期

Db.collection.createIndex( { “expireAt”: 1 }, { expireAfterSeconds: 0 } )

db.log_events.insert( { “expireAt”: new Date(Dec 22, 2019 02:01:00′),”logEvent”: 2,”logMessage”: “Success!”})

在expireAt指定的时间往后推迟expireAfterSeconds秒过期,也就是当前时间往后推迟60秒过期

db.log_events.insert( {“createdAt”: new Date(),”logEvent”: 2,”logMessage”: “Success!”} )

Db.collection.createIndex( { “expireAt”: 1 }, { expireAfterSeconds: 60 } )

Delete过期Tips2: 为何mongostat只能监控到从节点有delete操作,主节点没有?

原因是过期索引只在master主节点触发,触发后主节点会直接删除调用对应wiredtiger存储引擎接口做删除操作,不会走正常的客户端链接处理流程,因此主节点上看不到delete统计。

主节点过期delete后会生存对于的delete oplog信息,从节点通过拉取主节点oplog然后模拟对于client回放,这样就保证了主数据删除的同时从数据也得以删除,保证数据最终一致性。从节点模拟client回放过程将会走正常的client链接过程,因此会记录delete count统计,详见如下代码:

官方参考如下:

https://docs.mongodb.com/manual/tutorial/expire-data/

2.2 MongoDB配置优化(网络IO复用,网络IO和磁盘IO做分离)

由于集群tps高,同时整点有大量推送,因此整点并发会更高,mongodb默认的一个请求一个线程这种模式将会严重影响系统负载,该默认配置不适合高并发的读写应用场景。官方介绍如下:

2.2.1 MongoDB内部网络线程模型实现原理

mongodb默认网络模型架构是一个客户端链接,mongodb会创建一个线程处理该链接fd的所有读写请求及磁盘IO操作。

Mongodb默认网络线程模型不适合高并发读写原因如下:

在高并发的情况下,瞬间就会创建大量的线程,例如线上的这个集群,连接数会瞬间增加到1万左右,也就是操作系统需要瞬间创建1万个线程,这样系统load负载就会很高。

此外,当链接请求处理完,进入流量低峰期的时候,客户端连接池回收链接,这时候mongodb服务端就需要销毁线程,这样进一步加剧了系统负载,同时进一步增加了数据库的抖动,特别是在PHP这种短链接业务中更加明显,频繁的创建线程销毁线程造成系统高负债。

一个链接一个线程,该线程除了负责网络收发外,还负责写数据到存储引擎,整个网络I/O处理和磁盘I/O处理都由同一个线程负责,本身架构设计就是一个缺陷。

2.2.2 网络线程模型优化方法

为了适应高并发的读写场景,mongodb-3.6开始引入serviceExecutor: adaptive配置,该配置根据请求数动态调整网络线程数,并尽量做到网络IO复用来降低线程创建消耗引起的系统高负载问题。此外,加上serviceExecutor: adaptive配置后,借助boost:asio网络模块实现网络IO复用,同时实现网络IO和磁盘IO分离。这样高并发情况下,通过网络链接IO复用和mongodb的锁操作来控制磁盘IO访问线程数,最终降低了大量线程创建和消耗带来的高系统负载,最终通过该方式提升高并发读写性能。

2.2.3 网络线程模型优化前后性能对比

在该大流量集群中增加serviceExecutor: adaptive配置实现网络IO复用及网络IO与磁盘IO做分离后,该大流量集群时延大幅度降低,同时系统负载和慢日志也减少很多,具体如下:</

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值