【锁定直播】字节、华为云、阿里云等技术专家讨论如何将大模型接入 AIOps 解决实际问题,戳>>> 了解详情
写点什么

无需扩容成本,百亿集群数据过期性能优化看这就行

  • 2021-07-15
  • 本文字数:3838 字

    阅读完需:约 13 分钟

无需扩容成本,百亿集群数据过期性能优化看这就行

某百亿级 mongodb 业务只保存近期 7 天的数据,由于数据量大、流量高,数据过期删除点比较集中,同时不能错峰方式解决问题,因此如何利用最小物理成本来满足业务需求就成为了本集群性能优化的难点。


通过几轮和业务配合调优,包括存储引擎调优、数据删除方式调优、业务错峰读写等,最终完美解决了业务痛点,达到 ms 级业务读写访问。

一、业务背景

线上某业务数据量约 100 亿,白天为写流量高峰期,峰值写约 14W/s,如下图所示:




业务每天白天生产数据,同时凌晨批量拉取过去几天数据做大数据分析,整个集群只保存最近七天数据。


单条数据约 800 字节,如下所示:


1.   {  2.        "_id" : ObjectId("608592008bd3dad61675b491"),  3.        "deviceSn" : "xxxxxxxxxxxxx",  4.        "itemType" : 0,  5.        "module" : "xxxxxx",  6.        "userId" : "xxxxx",  7.        "callTimes" : NumberLong(2),  8.        "capacityAdd" : NumberLong(0),  9.        "capacityDelete" : ”xxxxxxxx”,  10.        "capacityDownload" :”xxxxxxxxxxxx”,  11.        "capacityModify" : ”xxxxxxxxxxxx”,  12.        "createTime" : NumberLong("1619366400003"),  13.        "expireAt" : ISODate("2021-05-02T22:53:45.497Z"),  14.        "numAdd" : NumberLong(2),  15.        "numDelete" : NumberLong(345),  16.        "numDownload" : NumberLong(43),  17.        "numModify" : NumberLong(3),  18.        "osVersion" : "xxxx",  19.        "reversedUserId" : "xxxxx",  20.        "updateTime" : NumberLong("1619366402106")  
复制代码

二、mongodb 资源评估及部署架构

通过和业务对接梳理,该集群规模及业务需求总结如下:

  • 数据量百亿级

  • 单条数据 800 字节,100 亿条预计 7.5T 数据

  • 读写分离

所有数据只保留七天

2.1 mongodb 资源评估

分片数及存储节点套餐规格选定评估过程如下:


  • 内存评估

我司都是容器化部署,以以往经验来看,mongodb 对内存消耗不高,历史百亿级以上 mongodb 集群单个容器最大内存基本上都是 64Gb,因此内存规格确定为 64G。


  • 分片评估

 业务流量峰值 10W/s 多,预计需要 3 个分片支持读写。


  • 磁盘评估

100 亿数据 7.5T,由于 mongodb 默认有高压缩,预计真实磁盘占用 2.5~3T 左右。三个分片,一个分片刚好 1T。


  • CPU 规格评估

由于容器调度套餐化限制,因此 CPU 只能限定为 16CPU(实际上用不了这么多 CPU)。


  • mongos 代理及 config server 规格评估

此外,由于分片集群还有 mongos 代理和 config server 复制集,因此还需要评估 mongos 代理和 config server 节点规格。由于 config server 只主要存储路由相关元数据,因此对磁盘、CUP、MEM 消耗都很低;mongos 代理只做路由转发只消耗 CPU,因此对内存和磁盘消耗都不高。最终,为了最大化节省成本,我们决定让一个代理和一个 config server 复用同一个容器,容器规格如下:


8CPU/8G 内存/50G 磁盘,一个代理和一个 config server 节点复用同一个容器。


分片及存储节点规格总结:4 分片/16CPU、64G 内存、1T 磁盘。


mongos 及 config server 规格总结:8CPU/8G 内存/50G 磁盘


节点类型

单个节点容器规格

说明

shardServer存储节点

16CPU64G内存、1T磁盘

1个分片存储约30亿数据

3个分片预计存储100亿数据

mongosconfig server节点

8CPU/8G内存/50G磁盘

一个代理和一个config server节点复用同一个容器

2.2 集群部署架构

该业务数据不是很重要,为了节省成本,因此我们采用 2+1 模式部署,也就是:2mongod+1arbiter 模式,同城机房部署,部署架构图如下图所示:



考虑到数据重要性不高,通过 2mongod+1arbiter 模式即可满足用户要求,同时可以最大化节省成本。

三、性能优化过程

该集群优化过程按照如下两个步骤优化:业务使用集群前的性能优化、业务使用过程中的性能优化。


业务提前建好查询对应的最优索引,同时创建过期索引:



db.dailyCloudOperateInfo.createIndex( { "createTime": 1 }, { expireAfterSeconds: 604800} )
复制代码

3.1  业务使用集群前的性能优化

和业务沟通确定,业务每条数据都携带有一个设备标识 userId,同时业务查询更新等都是根据 userId 维度查询该设备下面的单条或者一批数据,因此片建选择 userId。


  • 分片方式

为了充分散列数据到 3 个分片,因此选择 hash 分片方式,这样数据可以最大化散列,同时可以满足同一个 userId 数据落到同一个分片,保证查询效率。


  • 预分片

mongodb 如果分片片建为 hashed 分片,则可以提前做预分片,这样就可以保证数据写进来的时候比较均衡的写入多个分片。预分片的好处可以规避非预分片情况下的 chunk 迁移问题,最大化提升写入性能。



sh.shardCollection("cloud_track.dailyCloudOperateInfo", {userId:"hashed"}, false, { numInitialChunks: 8192} )
复制代码


  • 就近读

客户端增加 secondaryPreferred 配置,优先读从节点。


  • 禁用 enable Majority Read Concern

禁用该功能后 Read Concern majority 将会报错,Read Concern majority 功能注意是避免脏读,和业务沟通业务没该需求,因此可以直接关闭。


mongodb 默认使能了 enable Majority Read Concern,该功能开启对性能有一定影响,参考:


MongoDB readConcern 原理解析:

https://developer.aliyun.com/article/60553


OPPO 百万级高并发 MongoDB 集群性能数十倍提升优化实践:

https://mongoing.com/archives/29934


  • 存储引擎 cacheSize 规格选择

单个容器规格:16CPU、64G 内存、7T 磁盘,考虑到全量迁移过程中对内存压力,内存碎片等压力会比较大,为了避免 OOM,设置 cacheSize=42G。

3.2  业务使用过程中遇到的问题及性能优化

1)第一轮优化:存储引擎优化

业务高峰期主要是数据写入和更新,内存脏数据较多,当脏数据比例达到一定比例后用户读写请求对应线程将会阻塞,用户线程也会去淘汰内存中的脏数据 page,最终写性能下降明显。


wiredtiger 存储引擎 cache 淘汰策略相关的几个配置如下:



由于业务全量迁移数据是持续性的大流量写,而不是突发性的大流量写,因此 eviction_target、eviction_trigger、eviction_dirty_target、eviction_dirty_trigger 几个配置用处不大,这几个参数阀值只是在短时间突发流量情况下调整才有用。


但是,在持续性长时间大流量写的情况下,我们可以通过提高 wiredtiger 存储引擎后台线程数来解决脏数据比例过高引起的用户请求阻塞问题,淘汰脏数据的任务最终交由 evict 模块后台线程来完成。


全量大流量持续性写存储引擎优化如下:



db.adminCommand( { setParameter : 1, "wiredTigerEngineRuntimeConfig" : "eviction=(threads_min=4, threads_max=20)"})
复制代码

2)第一轮优化后存在的问题

经过存储引擎后台线程数的调优后,数据写入和更新瓶颈解决,写入和更新过程时延都很平滑。但是随着一周后开始数据过期,业务写开始大量抖动,如下所示:



从上图可以看出平均时延极端情况下甚至达到了几百 ms,这是业务完全接受不了的。通过 mongostat 监控发现如下现象:


  • 主节点 mongostat 监控统计



从上面的监控可以看出,三个分片的每个主节点只有 4000 左右的写更新操作(注意:实际上还有 4 万左右的 delete 操作,由于主节点过期 delete 不会统计,因此只能通过从节点查看,详见后面分析,实际单个分片主节点写和删除操作 4.4W/s),写流量很低。但是,监控中的脏数据比例持续性的超过 20%,超过 20%后业务的请求就需要进行脏数据淘汰,最终造成业务请求阻塞抖动。


通过前面的分析可以看出,业务正常峰值写入 3 个分片差不多 10W/s。一星期后,七天前的数据需要过期,这时候过期删除的 ops 也需要 delete 删除 10w/S,由于这时候新数据同样按照 10w/s 写入,因此集群就需要支持 20w/s 的 ops 操作才能支持。


显然,3 个分片支持不了持续性的 20w/s 左右的 ops 操作,因此如何不扩容情况下支撑业务需求将是一大难点。


  • 为何 ttl 过期主节点没用 delete 统计



上图为 mongodb 单机模块架构图,主节点默认启用一个 TTLMonitor 线程,借助查询引起模块实时扫描过期索引,然后把满足条件的数据删除。整个删除过程没有走 command 命令处理模块,而命令计数操作只会在 command 模块计数,因此主节点的过期删除不会有 delete 操作。


命令处理模块处理流程及其计数统计详见:

mongodb 内核源码模块化设计与实现专栏


更多 mongodb 模块化源码设计实现详见:

https://github.com/y123456yz/reading-and-annotate-mongodb-3.6

3.3  第二轮优化(过期删除放凌晨低峰期)-不可行

从前面的分析可以看出,我们三个分片支撑不了持续性 20W/S 的更新和删除操作,因此我们考虑业务改造使用方式,把过期删除确定到凌晨低峰期。


但是业务上线后出现其他的问题,由于业务凌晨会持续性的批量拉取分析过去几天的数据,如果过期和批量读数据叠加到一起,严重影响业务查询效率。最终,该方案不可行,如果不扩容,则需要其他方案。

3.4  第三轮方案优化(天维度建表,过期删表)

为了不增加成本,同时 3 个分片又支撑不了 20W/s 的读写删除等导致,为了尽量通过 3 个分片不扩容条件下来满足用户需求,因此转变方式,通过删表的方式来避免过期,具体实现如下:


  • 业务改造代码,以天为单位建表,每天的数据存到对应的表中。

  • 建表后业务启用预分片功能,确保数据均衡分布到三个分片。

  • 业务保存 8 天数据,第九天凌晨删除第一天的表


    该方案业务时延统计对应收益如下:




如上图所示,通过过期方式的优化,最终问题彻底解决,并且读写时延控制在 0.5ms-2ms。

四、优化收益总结

通过前面的一系列优化,最终没有扩容,并且解决了业务过期和大量数据写入更新引起的时延抖动问题,总体收益如下:


  • 改造优化前时延:经常几百 ms 抖动

  • 改造优化后时延:0.5-2ms


作者介绍

杨亚洲,前滴滴出行专家工程师,现任 OPPO 文档数据库 mongodb 负责人,负责数万亿级数据量文档数据库 mongodb 内核研发、性能优化及运维工作,一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。


本文转载自:dbaplus 社群(ID:dbaplus)

原文链接:无需扩容成本,百亿集群数据过期性能优化看这就行!


2021-07-15 13:001181

评论

发布
暂无评论
发现更多内容

架构实战营模块三作业

maybe

如何包容他人的多样性

escray

学习 极客时间 朱赟的技术管理课 7月日更

生产上数据库死锁,是该程序员祭天了

skow

Java MySQL 面试

《MySQL是怎样运行的》读后思考

MySQL

阿里大佬的「算法界Offer收割机」火爆Github,一夜获上万star

Java 编程 程序员 架构师 计算机

直接裂开!京东二面被问SpringBoot整合MongoDB,我不会啊

Java架构没有996

Java mongodb 程序员 后端 JAVA开发

关于线程的执行顺序,可能真的只是你以为的你以为

华为云开发者联盟

Java 线程 多线程 Thread 任务调度

Python开发篇——构建虚拟Python开发环境(Conda+Poetry)

DisonTangor

Python Anaconda

教你如何将二进制文件导入到数据库

华为云开发者联盟

数据库 数据 二进制 GaussDB(DWS) 二进制文件

聊聊数据仓库建设

水滴

数据仓库 数仓 数仓架构 主数据管理 标签体系

应聘高级Android工程师历程感言,你不懂还不学?

欢喜学安卓

android 程序员 面试 移动开发

花费近一年时间整理的Android核心知识清单,面试篇

欢喜学安卓

android 程序员 面试 移动开发

Linux安装与常用命令

IT视界

Linux linux命令 Linux安装

DataPipeline正式成为信创工委会会员单位!致力于为世界级用户提供更优质产品和服务

DataPipeline数见科技

大数据 数据融合 数据管理

钻石01:明心见性-如何由表及里精通线程池设计与原理

MetaThoughts

Java 多线程 并发

微软亚研院:如何看待计算机视觉未来的走向?

百度开发者中心

最佳实践 方法论 计算机视觉 语言 & 开发 文化 & 方法

数据,流通在没有船的港口

白洞计划

Axie区块链宠物游戏系统开发搭建

薇電13242772558

区块链

Optional 的使用会导致性能下降吗

小技术君

性能优化 Optional

当女性撰写科技新闻,她在报道什么?

脑极体

三十岁,像培养孩子一样培养自己。

南冥

英特尔陈伟:AIoT时代的新思维

E科讯

2021 挚物·AIoT产业领袖峰会召开,EMQ 映云科技喜获双料荣誉

EMQ映云科技

百度 华为 工业互联网 AIOT 边云协同

爱情,婚姻,与AI

脑极体

云图说|ROMA演进史:一个ROMA与应用之间不得不说的故事

华为云开发者联盟

华为云 应用 ROMA 云图说 应用使能

SpringBoot中时间格式化的5种方法!

王磊

spring springboot

《全国移动App第二季度安全研究报告》

InfoQ_11eaedef67e9

网络安全 移动安全 个人信息安全 APP安全

异常是怎么被处理的?这题的答案不在源码里面。

why技术

面试 JVM 字节码

架构之:REST和HATEOAS

程序那些事

架构 系统架构 Rest 软件架构

灵活运用分布式锁解决数据重复插入问题

vivo互联网技术

分布式锁 服务器 并发

队列Queue:任务间的消息读写,安排起来~

华为云开发者联盟

鸿蒙 数据结构 队列 Queue 消息

无需扩容成本,百亿集群数据过期性能优化看这就行_大数据_dbaplus社群_InfoQ精选文章