生产事故(MongoDB数据分布不均解决方案)

2024-06-07 18:58

本文主要是介绍生产事故(MongoDB数据分布不均解决方案),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

事故集合:

可以很明显可以看到我们这个集合的数据严重分布不均匀。

一共有8个分片,面对这个情况我首先想到的是手动拆分数据块,但这不是解决此问题的根本办法。

    造成此次生产事故的首要原因就是片键选择上的问题,由于片键选择失误,在数据量级不大的时候数据看起来还是很健康的,但随着数据量的暴涨,问题就慢慢浮出了水面,我们使用的组合片键并不是无规律的,片键内容是线性增长的,这就导致了数据的不正常聚集。由于数据分布不均匀,我们有两个分片的磁盘使用率接近80%,数据还在持续增长,这个问题必须尽快解决。

涉及到此次事故的集合一共有三个,总数据量加起来接近30T,数据总量300亿左右。

下面是我解决此问题的解决方案:

方案一:

第一步:创建一个新的分片表,片键我选择_id做hashed分片,并提前分好了数据块,降低在恢复期间频繁切割数据造成的服务器压力。

sh.shardCollection("loan_his.collection",{_id:"hashed"},false,{numInitialChunks:1024})

第二步:单独连接各个分片将8个分片的数据全量备份:

nohup mongodump -u loan_his -p loan_his --authenticationDatabase loan_his  -h ${replset} --db loan_his --collection ${collectionName} --query '{"txdt": { $lte: "2019-07-09"} }' -o ${bak_dir} &>>  ${log} &

你可能会问为什么不连接mongos,因为我在连接mongos做数据备份时出现了以下异常:

2019-07-08T16:10:03.886+0800	Failed: error writing data for collection `loan_his.ods_cus_trad` to disk: error reading collection: operation was interrupted

可能是因为集合内的数据坏块吧,此异常信息是我备份了将近70%的数据后突然抛出的异常信息。

除了这个原因,单独备份各个分片的数据后你能够自由控制恢复数据的时间窗口,不会因为恢复单个数据文件时间较长,突发意外情况导致恢复中断从头再来的窘境。能够根据服务器的状态避开高峰期来进行数据恢复。

备份期间我发现了有时候备份出来的总文档数和 db.collection.getShardDistribution() 查看的文档数不一致,我还以为是备份期间出了问题,但我删除当前备份文件后重新备份出来的文档数还是和之前一样。目前不知道是怎么回事,怀疑是坏的数据块引发的我问题,备份出来的数据一般会比原数据量多几万条数据,有时候会少一些。

第三步:恢复数据:

 mongorestore -u loan_his -p loan_his --authenticationDatabase loan_his -h 10.0.156.9:27017 --db loan_his  --collection  ${collectionName_two}  /mongodb/${collectionName}/replset_sh2/loan_his/${collectionName}.bson  &>>  ${log}

在恢复数据前千万要记得不要创建索引!否则性能极差,速度非常非常慢!在使用mongodump工具备份时,在数据文件的同级目录下会有一个 XXXXX.metadata.json 索引文件,默认会在数据恢复完毕后执行创建索引的操作。

此处有坑需要注意:因为备份出来的数据是由原表备份出来的,那这个索引文件也是原表的索引,由于原表我使用的是组合片键做的分片,所以在原表内会存在一个由片键组成的组合索引,并且不是后台创建的组合索引!!!这意味着如果你使用此索引文件来给新表创建索引,会造成这个集群处于阻塞状态,无法响应任何操作!!直至索引创建完毕。所以你可以将这个索引文件备份到其它目录以作参考,然后将原文件删除就可以了,恢复数据时不会有其它的问题。

如果恢复期间出现了意外情况导致恢复失败,比如节点宕机什么的,不需要担心,重新执行恢复程序,数据文件不会重复增加,因为备份出来的数据文件包含mongodb自带的 Objectld对象_id ,导入时,如果已存在此ID,将不会插入数据。注意:在不同集合是允许出现相同ID的,所以在使用方案二恢复数据时,新产生的数据不能通过新表A备份出来汇入新表C,需要通过原始数据文件重新导入。

第四步:创建索引:

待所有数据恢复完毕后再创建索引,一定要记得后台创建!!!"background":true !!!你也可以将索引拆分,一个一个的来。如果觉得此操作对业务影响较大,请看本文最后的解决方案。

mongo 10.0.156.2:27017/loan_his -uloan_his -ploan_his -eval 'db.getSiblingDB("loan_his").runCommand({createIndexes: "collection",indexes: [{"v":2,"key":{"_id":1},"name":"_id_","ns":"loan_his.collection"},{"v":2,"key":{"opnode":1.0,"txdt":1.0,"acct":1.0,"crdno":1.0},"name":"opnode_1_txdt_1_acct_1_crdno_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"txdt":1.0,"opnode":1.0,"acct":1.0,"crdno":1.0,"pbknum":1.0},"name":"txdt_1_opnode_1_acct_1_crdno_1_pbknum_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"acct":1.0,"txdt":1.0,"opnode":1.0},"name":"acct_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"crdno":1.0,"txdt":1.0,"opnode":1.0},"name":"crdno_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"pbknum":1.0,"txdt":1.0,"opnode":1.0},"name":"pbknum_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true}]})'

停止失控索引:

一旦你触发一个索引,简单的重启服务并不能解决这个问题,因为MongoDB会继续重启前的建索引的工作。如果之前你运行后台建索引任务,在服务重启后它会变成前台运行的任务。在这种情况下,重启会让问题变得更糟糕。MongoDB提供了选项“noIndexBuildRetry”,它会指示MongoDB重启后不再继续没建完的索引。如果不小心在前台创建了索引导致集群不可用,可以使用--noIndexBuildRetry 参数重启各个分片来停止索引的创建过程,只用重启主节点就可以了。如果是在后台创建索引,重启时记得加上--noIndexBuildRetry,否则重启后创建索引的线程会重新被唤醒,并由后台创建变为前台创建,导致整个集群不可用。

mongod -f $CONFIGFILE --noIndexBuildRetry

此方案迁移期间不用通知业务系统做变更,把数据迁移完毕后,通知业务系统将表名变更,弊端就是在你迁移的过程中数据还是会持续增长的,问题分片的磁盘容量会越来越少。

方案二:

为了避免在迁移期间数据仍在增长,导致数据还没迁移完毕磁盘就爆满的情况,可以选择停止往旧表B内写入数据,创建一个健康的新表A,新的数据往新表A内写,具体的查询方案需要应用系统的配合。然后将旧表B的数据迁移至新表C中,最终将新表A的数据汇入新表C , 完成数据迁移。此次迁移数据耗时共9个月!!!片键一定要慎重选择,因为我们使用的MongoDB是3.4.7版本的,不支持修改片键,最新版本支持片键的修改。

接下来介绍数据量较大时如何构建索引--减少业务最少影响

在数据量较大或请求量较大,直接建立索引对性能有显著影响时,可以利用复制集(数据量较大时一般为线上环境,使用复制集为必然选择或者使用分片.)中部分机器宕机不影响复制集工作的特性,继而建立索引。

(1)首先把 secondary server 停止,再注释 --replSet 参数,并且更改 MongoDB port 之后重新启动 MongoDB,这时候 MongoDB 将进入 standalone 模式;

(2).在 standalone 模式下运行命令 ensureIndex 建立索引,使用 foreground 方式运行也可以,建议使用background方式运行;

(3)建立索引完毕之后关闭 secondary server 按正常方式启动;

4.根据上述 1~3 的步骤轮流为 secondary 建立索引,最后把 primary server 临时转换为 secondary server,同样按 1~3 的方法建立索引,再把其转换为 primary server。

日志内容大致如下:

2019-09-24T18:51:39.003+0800 I -        [conn33]   Index Build: 838416900/876543270 95%
2019-09-24T20:10:08.360+0800 I INDEX    [conn33] 	 done building bottom layer, going to commit
2019-09-24T20:10:26.001+0800 I -        [conn33]   Index: (2/3) BTree Bottom Up Progress: 11684400/876543270 1%
done building bottom layer, going to commit

 

 

这篇关于生产事故(MongoDB数据分布不均解决方案)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040016

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

SpringBoot3匹配Mybatis3的错误与解决方案

《SpringBoot3匹配Mybatis3的错误与解决方案》文章指出SpringBoot3与MyBatis3兼容性问题,因未更新MyBatis-Plus依赖至SpringBoot3专用坐标,导致类冲... 目录SpringBoot3匹配MyBATis3的错误与解决mybatis在SpringBoot3如果

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

Linux部署中的文件大小写问题的解决方案

《Linux部署中的文件大小写问题的解决方案》在本地开发环境(Windows/macOS)一切正常,但部署到Linux服务器后出现模块加载错误,核心原因是Linux文件系统严格区分大小写,所以本文给大... 目录问题背景解决方案配置要求问题背景在本地开发环境(Windows/MACOS)一切正常,但部署到

Java中InputStream重复使用问题的几种解决方案

《Java中InputStream重复使用问题的几种解决方案》在Java开发中,InputStream是用于读取字节流的类,在许多场景下,我们可能需要重复读取InputStream中的数据,这篇文章主... 目录前言1. 使用mark()和reset()方法(适用于支持标记的流)2. 将流内容缓存到字节数组